天天看點

Srping Boot + Druid + Mybatis實作多資料源動态切換

作者:光州大少爺
  • pom.xml檔案引入
    <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
     <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
 </dependency>
           
  • application.properties檔案配置
## master db config begin
spring.datasource.druid.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.master.url= jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.druid.master.username=root
spring.datasource.druid.master.password=123456
# Druid configuration
# Druid其他配置
spring.datasource.druid.master.initial-size=5
spring.datasource.druid.master.max-active=20
spring.datasource.druid.master.min-idle=10
spring.datasource.druid.master.max-wait=10
spring.datasource.druid.master.filters=stat,wall
spring.datasource.druid.master.filter.stat.log-slow-sql=true
spring.datasource.druid.master.filter.stat.slow-sql-millis=2000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接配接,機關是毫秒
spring.datasource.druid.master.time-between-eviction-runs-millis=60000
#配置一個連接配接在池中的最小生存時間,機關是毫秒
spring.datasource.druid.master.min-evictable-idle-time-millis=300000
spring.datasource.druid.master.validation-query=SELECT 1
spring.datasource.druid.master.test-while-idle=true
spring.datasource.druid.master.test-on-borrow=false
spring.datasource.druid.master.test-on-return=false
#打開PSCache,并且指定每個連接配接上PSCache的大小
spring.datasource.druid.master.pool-prepared-statements=false
spring.datasource.druid.master.max-pool-prepared-statement-per-connection-size=20


## slave db config begin
spring.datasource.druid.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.slave.url= jdbc:mysql://localhost:3306/dbname2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.druid.slave.username=root
spring.datasource.druid.slave.password=123456
# Druid configuration
# Druid其他配置
spring.datasource.druid.slave.initial-size=5
spring.datasource.druid.slave.max-active=20
spring.datasource.druid.slave.min-idle=10
spring.datasource.druid.slave.max-wait=10
spring.datasource.druid.slave.filters=stat,wall
spring.datasource.druid.slave.filter.stat.log-slow-sql=true
spring.datasource.druid.slave.filter.stat.slow-sql-millis=2000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接配接,機關是毫秒
spring.datasource.druid.slave.time-between-eviction-runs-millis=60000
#配置一個連接配接在池中的最小生存時間,機關是毫秒
spring.datasource.druid.slave.min-evictable-idle-time-millis=300000
spring.datasource.druid.slave.validation-query=SELECT 1
spring.datasource.druid.slave.test-while-idle=true
spring.datasource.druid.slave.test-on-borrow=false
spring.datasource.druid.slave.test-on-return=false
#打開PSCache,并且指定每個連接配接上PSCache的大小
spring.datasource.druid.slave.pool-prepared-statements=false
spring.datasource.druid.slave.max-pool-prepared-statement-per-connection-size=20           
  • 定義資料源類型DataSourceType.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

資料源類型

詳細代碼:DataSourceType.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
public enum DataSourceType {
    MASTER, SLAVE
}           
  • 多資料源連接配接配置DynamicDataSourceConfig.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

資料源連接配接配置

詳細代碼:DynamicDataSourceConfig.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
@Configuration
public class DynamicDataSourceConfig {


    @Bean(name = "masterDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.druid.master")
    public DataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }


    @Bean(name = "slaveDataSource") // 聲明其為Bean執行個體
    @ConfigurationProperties(prefix = "spring.datasource.druid.slave")
    public DataSource slaveDataSource() {
        return DruidDataSourceBuilder.create().build();
    }


    /**
     * @return
     * @desc 動态配置多資料源
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource multipleDataSource(@Qualifier("masterDataSource") DataSource masterDb, @Qualifier("slaveDataSource") DataSource slaveDb) {
        DynamicDataSource multipleDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDb);
        targetDataSources.put(DataSourceType.SLAVE, slaveDb);
        //添加資料源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //設定預設資料源
        multipleDataSource.setDefaultTargetDataSource(masterDb);
        multipleDataSource.afterPropertiesSet();
        return multipleDataSource;
    }


    @Bean(name = "sqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        VFS.addImplClass(SpringBootVFS.class);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/mysql/*.xml"));
        return bean.getObject();
    }


    @Bean(name = "transactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }


    @Bean(name = "sqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
        //解決mybatis查詢過濾掉為空字段的問題
        sqlSessionTemplate.getConfiguration().setCallSettersOnNulls(true);
        return sqlSessionTemplate;
    }
}
           
  • 動态資料源配置DynamicDataSourceContextHolder.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

動态資料源配置

詳細代碼:DynamicDataSourceContextHolder.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
public class DynamicDataSourceContextHolder {

    /**
     * 線程獨立
     */
    private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();

    public static DataSourceType getDataSourceType() {
        DataSourceType dataSourceType = contextHolder.get() == null ? DataSourceType.MASTER : contextHolder.get();
        return dataSourceType;
    }

    public static void setDataSourceType(DataSourceType dataSourceType) {
        if (dataSourceType == null) throw new NullPointerException("未設定資料源");
        contextHolder.set(dataSourceType);
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}
           
  • 動态資料源切換實作DynamicDataSource.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

動态資料源切換實作

詳細代碼:DynamicDataSource.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        DataSourceType dataSourceType = DynamicDataSourceContextHolder.getDataSourceType();
        return dataSourceType;
    }
}
           
  • 定義多資料源注解DataSource.java

資料源注解

詳細代碼:DataSource.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {
    DataSourceType value() default DataSourceType.MASTER;
}           
  • AOP攔截多資料源注解DynamicDataSourceAspect.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

AOP攔截資料源注解

詳細代碼:DynamicDataSourceAspect.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
@Aspect
@Component
public class DynamicDataSourceAspect {

    private Logger LOG = LoggerFactory.getLogger(this.getClass());


    /**
     * 在Mapper層添加注解,實作切換資料源
     */
    @Pointcut("execution(public * com..*.dao.*.*(..))")
    public void dataSourcePointCut() {
    }


    @Before("dataSourcePointCut()")
    public void before(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        String method = joinPoint.getSignature().getName();
        Class<?>[] clazz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        try {
            Method m = clazz[0].getMethod(method, parameterTypes);
            //如果方法上存在切換資料源的注解,則根據注解内容進行資料源切換
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource data = m.getAnnotation(DataSource.class);
                DataSourceType dataSourceType = data.value();
                DynamicDataSourceContextHolder.setDataSourceType(dataSourceType);
                LOG.debug("current thread " + Thread.currentThread().getName() + " add " + dataSourceType + " to ThreadLocal");
            } else {
                LOG.debug("no data source specified, so use the default data source.");
            }
        } catch (Exception e) {
            LOG.error("switch datasource fail,current thread " + Thread.currentThread().getName() + " add data to ThreadLocal error", e);
        }
    }

    @After("dataSourcePointCut()")
    public void after(JoinPoint joinPoint) {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }

}
           
  • Dao層動态切換資料源UserDao.java
Srping Boot + Druid + Mybatis實作多資料源動态切換

Dao層實作

詳細代碼:UserDao.java

/**
 * @author 光州大少爺
 * @version 1.0
 */
@Repository
@Mapper
public interface UserDao {

    @DataSource(DataSourceType.MASTER)
    void saveUser(DataModel saveModel);

}           
  • 本篇文章完結,感謝你的觀看!