【十七】Spring Bootのマルチデータソース(AbstractRoutingDataSourceクラスを拡張し、データソース名に応じて動的に切り替えます.AOP注釈を使用して動的にデータソースを切り替えます)
一、springマルチデータソース構成の一般的な2つの方案:
1、springプロジェクトの起動時に直接複数の異なるデータソースを配置し、各データソースはそれぞれのsessionFactory、トランザクションマネージャを配置する.異なるDAOが異なるデータソースに対応することを指定します.(一般的にはmapperパスに基づいて指定されます.例えば、com.sid.mapper.Aというパケットの下にあるすべてのmapperを指定するにはdatasourceAデータソースを使用し、com.sid.mapper.Bというパケットの下にあるすべてのmapperを指定するにはdatasourceBデータソースを使用します).
2、複数の異なるデータソースを構成し、1つのセッションファクトリを使用する.ビジネスでデータ・ソースを自分で動的に切り替えます.同時セキュリティを保証するためには、threadlocalを使用して、マルチスレッド競合スイッチングデータソースの問題を実現する必要があります.
ここは2つ目です
二、コード
pom.xml
1、springプロジェクトの起動時に直接複数の異なるデータソースを配置し、各データソースはそれぞれのsessionFactory、トランザクションマネージャを配置する.異なるDAOが異なるデータソースに対応することを指定します.(一般的にはmapperパスに基づいて指定されます.例えば、com.sid.mapper.Aというパケットの下にあるすべてのmapperを指定するにはdatasourceAデータソースを使用し、com.sid.mapper.Bというパケットの下にあるすべてのmapperを指定するにはdatasourceBデータソースを使用します).
2、複数の異なるデータソースを構成し、1つのセッションファクトリを使用する.ビジネスでデータ・ソースを自分で動的に切り替えます.同時セキュリティを保証するためには、threadlocalを使用して、マルチスレッド競合スイッチングデータソースの問題を実現する必要があります.
ここは2つ目です
二、コード
pom.xml
4.0.0
com.sid
springboot
1.0-SNAPSHOT
jar
org.springframework.boot
spring-boot-starter-parent
1.5.8.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
5.1.38
com.alibaba
druid-spring-boot-starter
1.1.9
org.springframework.boot
spring-boot-maven-plugin
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.2
${basedir}/src/main/resources/generator/generatorConfig.xml
true
true
application.yml
server:
port: 8088
context-path: /sid
spring:
datasource:
# druid
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
druid:
one: # 1
url: jdbc:mysql://localhost:3306/sid
username: root
password: root
two: # 2
url: jdbc:mysql://localhost:3306/test
username: root
password: root
#
initialSize: 1
#
maxActive: 20
#
minIdle: 1
# ,
maxWait: 60000
# :
#1) Destroy , minEvictableIdleTimeMillis 。
#2) testWhileIdle , testWhileIdle
timeBetweenEvictionRunsMillis: 60000
# ,
minEvictableIdleTimeMillis: 300000
# SQL 。 validationQuery=null,testOnBorrow、testOnReturn、testWhileIdle 。
validationQuery: SELECT 1 FROM DUAL
# true, , 。 , timeBetweenEvictionRunsMillis, validationQuery 。
testWhileIdle: true
# validationQuery , 。
testOnBorrow: false
# validationQuery , 。
testOnReturn: false
# filters, sql ,'wall'
filters: stat,wall,slf4j
# connectProperties mergeSql ; SQL
#connectionProperties.druid.stat.mergeSql: true
#connectionProperties.druid.stat.slowSqlMillis: 5000
# DruidDataSource
#useGlobalDataSourceStat: true
#default-auto-commit: true
## , spring
mybatis:
mapper-locations: classpath:mapping/*.xml # : mapper xml
type-aliases-package: com.sid.model # :
configuration:
#log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # sql
/**
* @program: springboot
* @description:
* @author: Sid
* @date: 2018-11-22 13:59
* @since: 1.0
**/
public interface DataSourceNames {
String ONE = "ONE";
String TWO = "TWO";
}
AbstractRoutingDataSource ,
package com.sid.configuration.multi.datasource.dynamic;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* @program: springboot
* @description:
* AbstractRoutingDataSource ,
* DataSourcer ,
* key DataSource 。
* @author: Sid
* @date: 2018-11-22 13:59
* @since: 1.0
**/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* DataSource, defaultTargetDataSource
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map
package com.sid.configuration.multi.datasource.dynamic;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @program: springboot
* @description:
* @author: Sid
* @date: 2018-11-22 14:01
* @since: 1.0
**/
@Configuration
public class DynamicDataSourceConfig {
/**
* ChangeDataSource Bean
* */
@Bean
@ConfigurationProperties("spring.datasource.druid.one")
public DataSource oneDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
@Bean
@ConfigurationProperties("spring.datasource.druid.two")
public DataSource twoDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
/**
* , ChangeDataSource Bean
* */
@Bean
@Primary
public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource) {
Map
, DAO , :
TOW
DynamicDataSource.setDataSource(DataSourceNames.TWO);
//todo
userMapper.insert(user);
,
DynamicDataSource.clearDataSource();
, AOP, ,
package com.sid.configuration.multi.datasource.dynamic;
import java.lang.annotation.*;
/**
* @program: springboot
* @description:
* @author: Sid
* @date: 2018-11-22 14:02
* @since: 1.0
**/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {
String value() default DataSourceNames.ONE;
}
AOP
package com.sid.configuration.multi.datasource.dynamic;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @program: springboot
* @description: AOP
* @author: Sid
* @date: 2018-11-22 14:03
* @since: 1.0
**/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* : ChangeDataSource
*/
@Pointcut("@annotation(com.sid.configuration.multi.datasource.dynamic.ChangeDataSource)")
public void dataSourcePointCut() {}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
ChangeDataSource ds = method.getAnnotation(ChangeDataSource.class);
// @ChangeDataSource
DynamicDataSource.setDataSource(ds.value());
System.out.println(" : " + ds.value());
logger.debug("set datasource is " + ds.value());
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
addUserA @ChangeDataSource DataSource
package com.sid.service.impl;
import com.sid.configuration.multi.datasource.dynamic.ChangeDataSource;
import com.sid.configuration.multi.datasource.dynamic.DataSourceNames;
import com.sid.mapper.UserMapper;
import com.sid.model.User;
import com.sid.service.MultiDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @program: springboot
* @description:
* @author: Sid
* @date: 2018-11-22 14:20
* @since: 1.0
**/
@Service
public class MultiDataSourceImpl implements MultiDataSource {
@Autowired
private UserMapper userMapper;
@Override
public User addUserA(User user) {
userMapper.insert(user);
return user;
}
@ChangeDataSource(DataSourceNames.TWO)
@Override
public User addUserB(User user) {
userMapper.insert(user);
return user;
}
}