- 多数据源
- 动态数据源
多数据源
在pom中导入依赖
<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>1.3.2</version>
</dependency>
多数据源在配置文件时要加入你所需要的多个数据源的信息
mybatis.config-location=classpath:mybatis/mybatis-config.xml
spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.one.username=root
spring.datasource.one.password=root
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.two.username=root
spring.datasource.two.password=root
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
为两个数据源创建不同的mapper包路径
例如:原来的单数据源将接口文档建在mapper包下,而多数据源建在mapper包下在分出来one包和two包,放置接口文件,对应的映射文件也是如此。
配置第⼀个数据源,新建 DataSource1Config。值得注意的是在配置数据源时要用@Primary指定主数据源
@Configuration
@MapperScan(basePackages = "com.neo.mapper.one", sqlSessionTemplateRef = "oneSqlSessionTemplate")
public class DataSource1Config {
@Bean(name = "oneDataSource")
@ConfigurationProperties(prefix = "spring.datasource.one")
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "oneSqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("oneDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/one/*.xml"));
return bean.getObject();
}
@Bean(name = "oneTransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("oneDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "oneSqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("oneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
basePackages 指明 Mapper 地址。
sqlSessionTemplateRef 指定 Mapper 路径下注⼊的 sqlSessionTemplate。
第⼆个数据源配置
DataSource2Config 的配置和上⾯类似,⽅法上需要去掉 @Primary 注解,替换对应的数据源和 Mapper 路
径即可。下⾯是 DataSource2Config
@Configuration
@MapperScan(basePackages = "com.neo.mapper.two", sqlSessionTemplateRef = "twoSqlSessionTemplate")
public class DataSource2Config {
@Bean(name = "twoDataSource")
@ConfigurationProperties(prefix = "spring.datasource.two")
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "twoSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("twoDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/two/*.xml"));
return bean.getObject();
}
@Bean(name = "twoTransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("twoDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "twoSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("twoSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
接下来就是注入
@Autowired
private User1Mapper user1Mapper;
@Autowired
private User2Mapper user2Mapper;
动态数据源
使用spring aop来动态切换数据源
Spring动态切换数据库的原理是通过继承AbstractRoutingDataSource重写determineCurrentLookupKey()方法,来决定使用那个数据库。在开启事务之前,通过改变lookupKey来达到切换数据源目的。
先写DataSourceContextHolder用来保存当前线程的数据库源。
public class DataSourceContextHolder {
/**
* 默认数据源
*/
public static final String DEFAULT_DS = "masterRds";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
DynamicDataSource
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDB();
}
}
配置数据源
DataSourceConfig
@Component
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.master.url}")
String masterUrl;
@Value("${spring.datasource.master.username}")
String masterUsername;
@Value("${spring.datasource.master.password}")
String masterPassword;
@Value("${spring.datasource.master.driver-class-name}")
String masterDriverClassName;
@Bean(name = "masterRds")
public DataSource masterDs() {
DruidDataSource datasource1 = new DruidDataSource();
datasource1.setUrl(masterUrl);
datasource1.setUsername(masterUsername);
datasource1.setPassword(masterPassword);
datasource1.setDriverClassName(masterDriverClassName);
return datasource1;
}
@Value("${spring.datasource.cluster-rds.url}")
String clusterUrl;
@Value("${spring.datasource.cluster-rds.username}")
String clusterUsername;
@Value("${spring.datasource.cluster-rds.password}")
String clusterPassword;
@Value("${spring.datasource.cluster-rds.driver-class-name}")
String clusterDriverClassName;
@Bean(name = "clusterRds")
public DataSource clusterRds() {
DruidDataSource datasource1 = new DruidDataSource();
datasource1.setUrl(clusterUrl);
datasource1.setUsername(clusterUsername);
datasource1.setPassword(clusterPassword);
datasource1.setDriverClassName(clusterDriverClassName);
return datasource1;
}
@Value("${spring.datasource.user-datasouce.url}")
String userUrl;
@Value("${spring.datasource.user-datasouce.username}")
String userUsername;
@Value("${spring.datasource.user-datasouce.password}")
String userPassword;
@Value("${spring.datasource.user-datasouce.driver-class-name}")
String userDriverClassName;
@Bean(name = "userDs")
public DataSource userDs() {
DruidDataSource datasource1 = new DruidDataSource();
datasource1.setUrl(userUrl);
datasource1.setUsername(userUsername);
datasource1.setPassword(userPassword);
datasource1.setDriverClassName(userDriverClassName);
return datasource1;
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
*
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDs());
// 配置多数据源
Map<Object, Object> dsMap = Maps.newHashMap();
dsMap.put("masterRds", masterDs());
dsMap.put("clusterRds", clusterRds());
dsMap.put("userDs",userDs());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* 配置@Transactional注解事物
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
使用aop来进行切换
定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface DS {
String value() default "masterRds";
}
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(ds)")
public void beforeSwitchDS(JoinPoint point, DS ds) {
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
@AfterReturning("@annotation(ds)")
public void afterSwitchDS(JoinPoint point, DS ds) {
String db = DataSourceContextHolder.getDB();
DataSourceContextHolder.clearDB();
DataSourceContextHolder.setDB(DataSourceContextHolder.DEFAULT_DS);
String db1 = DataSourceContextHolder.getDB();
}
}
使用
@DS("clusterRds")
public int selectNum() {
return authCenterMapper.selectNum();
}