欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot自定義動(dòng)態(tài)數(shù)據(jù)源的流程步驟

 更新時(shí)間:2024年06月25日 08:46:26   作者:AhYi8  
動(dòng)態(tài)數(shù)據(jù)源,本質(zhì)上是把多個(gè)數(shù)據(jù)源存儲(chǔ)在一個(gè)?Map?中,當(dāng)需要使用某一個(gè)數(shù)據(jù)源時(shí),使用?key?獲取指定數(shù)據(jù)源進(jìn)行處理,本文將給大家介紹一下SpringBoot自定義動(dòng)態(tài)數(shù)據(jù)源的流程步驟,需要的朋友可以參考下

1. 原理

動(dòng)態(tài)數(shù)據(jù)源,本質(zhì)上是把多個(gè)數(shù)據(jù)源存儲(chǔ)在一個(gè) Map 中,當(dāng)需要使用某一個(gè)數(shù)據(jù)源時(shí),使用 key 獲取指定數(shù)據(jù)源進(jìn)行處理。而在 Spring 中已提供了抽象類 AbstractRoutingDataSource 來(lái)實(shí)現(xiàn)此功能,繼承 AbstractRoutingDataSource 類并覆寫其 determineCurrentLookupKey() 方法監(jiān)聽(tīng)獲取 key 即可,該方法只需要返回?cái)?shù)據(jù)源 key 即可,也就是存放數(shù)據(jù)源的 Mapkey

因此,我們?cè)趯?shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源的,只需要繼承它,實(shí)現(xiàn)自己的獲取數(shù)據(jù)源邏輯即可。AbstractRoutingDataSource 頂級(jí)繼承了 DataSource,所以它也是可以做為數(shù)據(jù)源對(duì)象,因此項(xiàng)目中使用它作為主數(shù)據(jù)源。

1.1. AbstractRoutingDataSource 源碼解析

        public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
        // 目標(biāo)數(shù)據(jù)源 map 集合,存儲(chǔ)將要切換的多數(shù)據(jù)源 bean 信息,可以通過(guò) setTargetDataSource(Map<Object, Object> mp) 設(shè)置
        @Nullable
        private Map<Object, Object> targetDataSources;
        // 未指定數(shù)據(jù)源時(shí)的默認(rèn)數(shù)據(jù)源對(duì)象,可以通過(guò) setDefaultTargetDataSouce(Object obj) 設(shè)置
        @Nullable
        private Object defaultTargetDataSource;
  ...
        // 數(shù)據(jù)源查找接口,通過(guò)該接口的 getDataSource(String dataSourceName) 獲取數(shù)據(jù)源信息
        private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        //解析 targetDataSources 之后的 DataSource 的 map 集合
        @Nullable
        private Map<Object, DataSource> resolvedDataSources;
        @Nullable
        private DataSource resolvedDefaultDataSource;

        //將 targetDataSources 的內(nèi)容轉(zhuǎn)化一下放到 resolvedDataSources 中,將 defaultTargetDataSource 轉(zhuǎn)為 DataSource 賦值給 resolvedDefaultDataSource
        public void afterPropertiesSet() {
            //如果目標(biāo)數(shù)據(jù)源為空,會(huì)拋出異常,在系統(tǒng)配置時(shí)應(yīng)至少傳入一個(gè)數(shù)據(jù)源
            if (this.targetDataSources == null) {
                throw new IllegalArgumentException("Property 'targetDataSources' is required");
            } else {
                //初始化 resolvedDataSources 的大小
                this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size());
                //遍歷目標(biāo)數(shù)據(jù)源信息 map 集合,對(duì)其中的 key,value 進(jìn)行解析
                this.targetDataSources.forEach((key, value) -> {
                    // resolveSpecifiedLookupKey 方法沒(méi)有做任何處理,只是將 key 繼續(xù)返回
                    Object lookupKey = this.resolveSpecifiedLookupKey(key);
                    // 將目標(biāo)數(shù)據(jù)源 map 集合中的 value 值(Druid 數(shù)據(jù)源信息)轉(zhuǎn)為 DataSource 類型
                    DataSource dataSource = this.resolveSpecifiedDataSource(value);
                    // 將解析之后的 key,value 放入 resolvedDataSources 集合中
                    this.resolvedDataSources.put(lookupKey, dataSource);
                });
                if (this.defaultTargetDataSource != null) {
                    // 將默認(rèn)目標(biāo)數(shù)據(jù)源信息解析并賦值給 resolvedDefaultDataSource
                    this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
                }

            }
        }

        protected Object resolveSpecifiedLookupKey(Object lookupKey) {
            return lookupKey;
        }

        protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
            if (dataSource instanceof DataSource) {
                return (DataSource)dataSource;
            } else if (dataSource instanceof String) {
                return this.dataSourceLookup.getDataSource((String)dataSource);
            } else {
                throw new IllegalArgumentException("Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
            }
        }

        // 因?yàn)?AbstractRoutingDataSource 繼承 AbstractDataSource,而 AbstractDataSource 實(shí)現(xiàn)了 DataSource 接口,所有存在獲取數(shù)據(jù)源連接的方法
        public Connection getConnection() throws SQLException {
            return this.determineTargetDataSource().getConnection();
        }

        public Connection getConnection(String username, String password) throws SQLException {
            return this.determineTargetDataSource().getConnection(username, password);
        }

  // 最重要的一個(gè)方法,也是 DynamicDataSource 需要實(shí)現(xiàn)的方法
        protected DataSource determineTargetDataSource() {
            Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            // 調(diào)用實(shí)現(xiàn)類中重寫的 determineCurrentLookupKey 方法拿到當(dāng)前線程要使用的數(shù)據(jù)源的名稱
            Object lookupKey = this.determineCurrentLookupKey();
            // 去解析之后的數(shù)據(jù)源信息集合中查詢?cè)摂?shù)據(jù)源是否存在,如果沒(méi)有拿到則使用默認(rèn)數(shù)據(jù)源 resolvedDefaultDataSource
            DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
            if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
                dataSource = this.resolvedDefaultDataSource;
            }

            if (dataSource == null) {
                throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
            } else {
                return dataSource;
            }
        }

        @Nullable
        protected abstract Object determineCurrentLookupKey();
    }

1.2. 關(guān)鍵類說(shuō)明

忽略掉 controller/service/entity/mapper/xml介紹。

  • application.yml:數(shù)據(jù)源配置文件。但是如果數(shù)據(jù)源比較多的話,根據(jù)實(shí)際使用,最佳的配置方式還是獨(dú)立配置比較好。
  • DynamicDataSourceRegister:動(dòng)態(tài)數(shù)據(jù)源注冊(cè)配置文件
  • DynamicDataSource:動(dòng)態(tài)數(shù)據(jù)源配置類,繼承自 AbstractRoutingDataSource
  • TargetDataSource:動(dòng)態(tài)數(shù)據(jù)源注解,切換當(dāng)前線程的數(shù)據(jù)源
  • DynamicDataSourceAspect:動(dòng)態(tài)數(shù)據(jù)源設(shè)置切面,環(huán)繞通知,切換當(dāng)前線程數(shù)據(jù)源,方法注解優(yōu)先
  • DynamicDataSourceContextHolder:動(dòng)態(tài)數(shù)據(jù)源上下文管理器,保存當(dāng)前數(shù)據(jù)源的 key,默認(rèn)數(shù)據(jù)源名,所有數(shù)據(jù)源 key

1.3. 開(kāi)發(fā)流程

  • 添加配置文件,設(shè)置默認(rèn)數(shù)據(jù)源配置,和其他數(shù)據(jù)源配置
  • 編寫 DynamicDataSource 類,繼承 AbstractRoutingDataSource 類,并實(shí)現(xiàn) determineCurrentLookupKey() 方法
  • 編寫 DynamicDataSourceHolder 上下文管理類,管理當(dāng)前線程的使用的數(shù)據(jù)源,及所有數(shù)據(jù)源的 key;
  • 編寫 DynamicDataSourceRegister 類通過(guò)讀取配置文件動(dòng)態(tài)注冊(cè)多數(shù)據(jù)源,并在啟動(dòng)類上導(dǎo)入(@Import)該類
  • 自定義數(shù)據(jù)源切換注解 TargetDataSource,并實(shí)現(xiàn)相應(yīng)的切面,環(huán)繞通知切換當(dāng)前線程數(shù)據(jù)源,注解優(yōu)先級(jí)(DynamicDataSourceHolder.setDynamicDataSourceKey() > Method > Class

2. 實(shí)現(xiàn)

2.1. 引入 Maven 依賴

<!-- web 模塊依賴 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring 核心 aop 模塊依賴 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Druid 數(shù)據(jù)源連接池依賴 -->
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-starter</artifactId>
 <version>1.2.8</version>
</dependency>
<!-- mybatis 依賴 -->
<dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>2.2.2</version>
</dependency>
<!-- mysql驅(qū)動(dòng) -->
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>8.0.24</version>
</dependency>
<!-- lombok 模塊依賴 -->
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
</dependency>
<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-text</artifactId>
 <version>1.10.0</version>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
</dependency>

2.2. application.yml 配置文件

spring:
  datasource:
 type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding-utf8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
custom:
  datasource:
    names: ds1,ds2
    ds1:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/content_center?useUnicode
      username: root
      password: root
    ds2:
   type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/trade?useUnicode
      username: root
      password: root

2.3. 創(chuàng)建 DynamicDataSource 繼承 AbstractRoutingDataSource 類

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

/**
 * @Description: 繼承Spring AbstractRoutingDataSource 實(shí)現(xiàn)路由切換
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DynamicDataSource extends AbstractRoutingDataSource {

 /**
  * 決定當(dāng)前線程使用哪種數(shù)據(jù)源
  * @return 數(shù)據(jù)源 key
  */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

2.4. 編寫 DynamicDataSourceHolder 類,管理 DynamicDataSource 上下文

import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 動(dòng)態(tài)數(shù)據(jù)源上下文管理
 */
public class DynamicDataSourceHolder {
    // 存放當(dāng)前線程使用的數(shù)據(jù)源類型信息
    private static final ThreadLocal<String> DYNAMIC_DATASOURCE_KEY = new ThreadLocal<String>();
    // 存放數(shù)據(jù)源 key
    private static final List<String> DATASOURCE_KEYS = new ArrayList<String>();
    // 默認(rèn)數(shù)據(jù)源 key
    public static final String DEFAULT_DATESOURCE_KEY = "master";

    //設(shè)置數(shù)據(jù)源
    public static void setDynamicDataSourceType(String key) {
        DYNAMIC_DATASOURCE_KEY.set(key);
    }

    //獲取數(shù)據(jù)源
    public static String getDynamicDataSourceType() {
        return DYNAMIC_DATASOURCE_KEY.get();
    }

    //清除數(shù)據(jù)源
    public static void removeDynamicDataSourceType() {
        DYNAMIC_DATASOURCE_KEY.remove();
    }

 public static void addDataSourceKey(String key) {
  DATASOURCE_KEYS.add(key)
 }

    /**
     * 判斷指定 key 當(dāng)前是否存在
     *
     * @param key
     * @return boolean
     */
    public static boolean containsDataSource(String key){
        return DATASOURCE_KEYS.contains(key);
    }
}

2.5. 編寫 DynamicDataSourceRegister 讀取配置文件注冊(cè)多數(shù)據(jù)源

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;

/**
 * @Description: 注冊(cè)動(dòng)態(tài)數(shù)據(jù)源
 * 初始化數(shù)據(jù)源和提供了執(zhí)行動(dòng)態(tài)切換數(shù)據(jù)源的工具類
 * EnvironmentAware(獲取配置文件配置的屬性值)
 */
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
 private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
    // 指定默認(rèn)數(shù)據(jù)源類型 (springboot2.0 默認(rèn)數(shù)據(jù)源是 hikari 如何想使用其他數(shù)據(jù)源可以自己配置)
    // private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
    private static final String DEFAULT_DATASOURCE_TYPE = "com.alibaba.druid.pool.DruidDataSource";
    // 默認(rèn)數(shù)據(jù)源
    private DataSource defaultDataSource;
    // 用戶自定義數(shù)據(jù)源
    private Map<String, DataSource> customDataSources  = new HashMap<>();

    /**
     * 加載多數(shù)據(jù)源配置
     * @param env 當(dāng)前環(huán)境
     */
    @Override
    public void setEnvironment(Environment env) {
        initDefaultDataSource(env);
        initCustomDataSources(env);
    }



    /**
     * 初始化主數(shù)據(jù)源
     * @param env
     */
    private void initDefaultDataSource(Environment env) {
        // 讀取主數(shù)據(jù)源
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("type", env.getProperty("spring.datasource.type", DEFAULT_DATASOURCE_TYPE));
        dsMap.put("driver", env.getProperty("spring.datasource.driver-class-name"));
        dsMap.put("url", env.getProperty("spring.datasource.url"));
        dsMap.put("username", env.getProperty("spring.datasource.username"));
        dsMap.put("password", env.getProperty("spring.datasource.password"));
        defaultDataSource = buildDataSource(dsMap);
    }


    /**
     * 初始化更多數(shù)據(jù)源
     * @param env
     */
    private void initCustomDataSources(Environment env) {
        // 讀取配置文件獲取更多數(shù)據(jù)源
        String dsPrefixs = env.getProperty("custom.datasource.names");
        if (!StringUtils.isBlank(dsPrefixs)) {
         for (String dsPrefix : dsPrefixs.split(",")) {
          dsPrefix = fsPrefix.trim()
          if (!StringUtils.isBlank(dsPrefix)) {
           Map<String, Object> dsMap = new HashMap<>();
           dsMap.put("type", env.getProperty("custom.datasource." + dsPrefix + ".type", DEFAULT_DATASOURCE_TYPE));
              dsMap.put("driver", env.getProperty("custom.datasource." + dsPrefix + ".driver-class-name"));
              dsMap.put("url", env.getProperty("custom.datasource." + dsPrefix + ".url"));
              dsMap.put("username", env.getProperty("custom.datasource." + dsPrefix + ".username"));
              dsMap.put("password", env.getProperty("custom.datasource." + dsPrefix + ".password"));
              DataSource ds = buildDataSource(dsMap);
              customDataSources.put(dsPrefix, ds);
          }
         }
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        // 將主數(shù)據(jù)源添加到更多數(shù)據(jù)源中
        targetDataSources.put(DynamicDataSourceHolder.DEFAULT_DATASOURCE_KEY, defaultDataSource);
        DynamicDataSourceHolder.addDataSourceKey(DynamicDataSourceHolder.DEFAULT_DATASOURCE_KEY);
        // 添加更多數(shù)據(jù)源
        targetDataSources.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.addDataSourceKey(key);
        }

        // 創(chuàng)建 DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition); // 注冊(cè)到 Spring 容器中
        LOGGER.info("Dynamic DataSource Registry");
    }

    /**
     * 創(chuàng)建 DataSource
     * @param dsMap 數(shù)據(jù)庫(kù)配置參數(shù)
     * @return DataSource
     */
    public DataSource buildDataSource(Map<String, Object> dsMap) {
        try {
            Object type = dsMap.get("type");
            if (type == null)
                type = DEFAULT_DATASOURCE_TYPE;// 默認(rèn)DataSource

            Class<? extends DataSource> dataSourceType = (Class<? extends DataSource>)Class.forName((String)type);
            String driverClassName = String.valueOf(dsMap.get("driver"));
            String url = String.valueOf(dsMap.get("url"));
            String username = String.valueOf(dsMap.get("username"));
            String password = String.valueOf(dsMap.get("password"));

            // 自定義 DataSource 配置
            DataSourceBuilder<? extends DataSource> factory = DataSourceBuilder.create()
                    .driverClassName(driverClassName)
                    .url(url)
                    .username(username)
                    .password(password)
                    .type(dataSourceType);
            return factory.build();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2.6. 在啟動(dòng)器類上添加 @Import,導(dǎo)入 register 類

// 注冊(cè)動(dòng)態(tài)多數(shù)據(jù)源
@Import({ DynamicDataSourceRegister.class })
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.7. 自定義注解 @TargetDataSource

/**
 * 自定義多數(shù)據(jù)源切換注解
 * 優(yōu)先級(jí):DynamicDataSourceHolder.setDynamicDataSourceKey() > Method > Class
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切換數(shù)據(jù)源名稱
     */
    public String value() default DynamicDataSourceHolder.DEFAULT_DATESOURCE_KEY;
}

2.8. 定義切面攔截 @TargetDataSource

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;

@Aspect
// 保證在 @Transactional 等注解前面執(zhí)行
@Order(-1)
@Component
public class DataSourceAspect {

    // 設(shè)置 DataSource 注解的切點(diǎn)表達(dá)式
    @Pointcut("@annotation(com.ayi.config.datasource.DynamicDataSource)")
    public void dynamicDataSourcePointCut(){

    }

    //環(huán)繞通知
    @Around("dynamicDataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        String key = getDefineAnnotation(joinPoint).value();
        if (!DynamicDataSourceHolder.containsDataSource(key)) {
         LOGGER.error("數(shù)據(jù)源[{}]不存在,使用默認(rèn)數(shù)據(jù)源[{}]", key, DynamicDataSourceHolder.DEFAULT_DATESOURCE_KEY)
         key = DynamicDataSourceHolder.DEFAULT_DATESOURCE_KEY;
        }
        DynamicDataSourceHolder.setDynamicDataSourceKey(key);
        try {
            return joinPoint.proceed();
        } finally {
            DynamicDataSourceHolder.removeDynamicDataSourceKey();
        }
    }

    /**
     * 先判斷方法的注解,后判斷類的注解,以方法的注解為準(zhǔn)
     * @param joinPoint 切點(diǎn)
     * @return TargetDataSource
     */
    private TargetDataSource getDefineAnnotation(ProceedingJoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        TargetDataSource dataSourceAnnotation = methodSignature.getMethod().getAnnotation(TargetDataSource.class);
        if (Objects.nonNull(methodSignature)) {
            return dataSourceAnnotation;
        } else {
            Class<?> dsClass = joinPoint.getTarget().getClass();
            return dsClass.getAnnotation(TargetDataSource.class);
        }
    }

}

以上就是SpringBoot自定義動(dòng)態(tài)數(shù)據(jù)源的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot動(dòng)態(tài)數(shù)據(jù)源的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于Java反射機(jī)制 你需要知道的事情

    關(guān)于Java反射機(jī)制 你需要知道的事情

    這篇文章主要介紹了Java反射機(jī)制的相關(guān)內(nèi)容,涉及了class類的動(dòng)態(tài)加載,獲取成員變量、構(gòu)造函數(shù)信息等信息,需要的朋友可以參考下。
    2017-09-09
  • Java Stream流知識(shí)總結(jié)

    Java Stream流知識(shí)總結(jié)

    這篇文章主要介紹了Java Stream流的相關(guān)知識(shí),文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Mybatis實(shí)現(xiàn)插入數(shù)據(jù)后返回主鍵過(guò)程解析

    Mybatis實(shí)現(xiàn)插入數(shù)據(jù)后返回主鍵過(guò)程解析

    這篇文章主要介紹了Mybatis實(shí)現(xiàn)插入數(shù)據(jù)后返回主鍵過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • MyBatis中example.createCriteria()方法的具體使用

    MyBatis中example.createCriteria()方法的具體使用

    本文詳細(xì)介紹了MyBatis的Example工具的使用方法,包括鏈?zhǔn)秸{(diào)用指定字段、設(shè)置查詢條件、支持多種查詢方式等,還介紹了mapper的crud方法、and/or方法的使用,以及如何進(jìn)行多條件和多重條件查詢,感興趣的可以了解一下
    2024-10-10
  • Java Spring Boot 集成Zookeeper

    Java Spring Boot 集成Zookeeper

    這篇文章主要介紹了Java Spring Boot 集成Zookeeper,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Java實(shí)戰(zhàn)入門之雙色球彩票小游戲

    Java實(shí)戰(zhàn)入門之雙色球彩票小游戲

    這篇文章主要介紹了Java實(shí)戰(zhàn)入門之雙色球彩票,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • springboot?整合sentinel的示例代碼

    springboot?整合sentinel的示例代碼

    本文主要介紹了springboot?整合sentinel的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Java中使用增強(qiáng)for循環(huán)的實(shí)例方法

    Java中使用增強(qiáng)for循環(huán)的實(shí)例方法

    在本篇文章里小編給大家整理是的關(guān)于Java中如何使用增強(qiáng)for循環(huán)的實(shí)例內(nèi)容以及相關(guān)代碼,需要的朋友們可以學(xué)習(xí)下。
    2019-08-08
  • Java多線程案例實(shí)戰(zhàn)之定時(shí)器的實(shí)現(xiàn)

    Java多線程案例實(shí)戰(zhàn)之定時(shí)器的實(shí)現(xiàn)

    在Java中可以使用多線程和定時(shí)器來(lái)實(shí)現(xiàn)定時(shí)任務(wù),下面這篇文章主要給大家介紹了關(guān)于Java多線程案例之定時(shí)器實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • mybatis中bind標(biāo)簽和concat的使用說(shuō)明

    mybatis中bind標(biāo)簽和concat的使用說(shuō)明

    這篇文章主要介紹了mybatis中bind標(biāo)簽和concat的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12

最新評(píng)論