扣扣分享交流群:1125844267
提示:
本文主要流程参考博客:https://www.cnblogs.com/xifengxiaoma/p/9888240.html
集成druid数据库连接池参考博客:https://www.cnblogs.com/dyc940210/p/9334589.html
配置步骤
1.配置文件中配置多数据源信息
代码如下:
datasource:
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
master:
url: jdbc:mysql://ip:3306/数据库?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: 用户名
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
slave:
url: jdbc:mysql://ip:3306/数据库?useSSL=false&useUnicode=yes&characterEncoding=UTF-8
username: 用户名
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
2.启动类配置
主要是添加注解,禁用数据源默认自动配置
代码如下:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3.mybatis配置
代码如下:
/**
* mybatis配置
*/
@Configuration
@MapperScan(basePackages = {"cn.xxxx.xx.mapper"})
public class MybatisConfig {
//数据源类型:连接池
@Value("${spring.datasource.type}")
private Class<? extends DataSource> dataSourceType;
@Bean("master")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource master() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean("slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slave() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put("master", master());
dataSourceMap.put("slave", slave());
// 将 master 数据源作为默认指定的数据源
dynamicDataSource.setDefaultDataSource(master());
// 将 master 和 slave 数据源作为指定的数据源
dynamicDataSource.setDataSources(dataSourceMap);
return dynamicDataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
sessionFactory.setDataSource(dynamicDataSource());
sessionFactory.setTypeAliasesPackage("cn.xxxx.xx.bean.*");
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactory.setMapperLocations(resolver.getResources("classpath*:**/mapping/*.xml")); // 扫描映射文件
return sessionFactory;
}
@Bean
public PlatformTransactionManager transactionManager() {
// 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
return new DataSourceTransactionManager(dynamicDataSource());
}
}
4.动态数据源实现类
代码如下:
/**
* 动态数据源实现类
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
* 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
*/
@Override
protected DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
/**
* 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
/**
* 设置默认数据源
* @param defaultDataSource
*/
public void setDefaultDataSource(Object defaultDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
}
/**
* 设置数据源
* @param dataSources
*/
public void setDataSources(Map<Object, Object> dataSources) {
super.setTargetDataSources(dataSources);
// 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
}
}
5.动态数据源上下文
代码如下:
/**
* 动态数据源上下文
*/
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 master 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return "master";
}
};
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
* @param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/**
* 获取数据源
* @return
*/
public static String getDataSourceKey() {
return contextHolder.get();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {
contextHolder.remove();
}
/**
* 判断是否包含数据源
* @param key 数据源key
* @return
*/
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
/**
* 添加数据源keys
* @param keys
* @return
*/
public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
return dataSourceKeys.addAll(keys);
}
}
那么到了这一步其实就可以实现数据源的切换,只要调用 DynamicDataSourceContextHolder.setDataSourceKey(key) 就可以完成了,使用完成后再设置回默认数据源DynamicDataSourceContextHolder.clearDataSourceKey()。
6.注解AOP配置
使用注解和AOP的方式简化我们的操作,在添加注解的方法前后分别切换数据库。
代码如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 数据源key值
* @return
*/
String value();
}
@Aspect
@Order(-1) // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {
/**
* 切换数据源
* @param point
* @param dataSource
*/
@Before("@annotation(dataSource))")
public void switchDataSource(JoinPoint point, DataSource dataSource) {
if (!DynamicDataSourceContextHolder.containDataSourceKey(dataSource.value())) {
System.out.println("DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value());
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
System.out.println("Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]");
}
}
/**
* 重置数据源
* @param point
* @param dataSource
*/
@After("@annotation(dataSource))")
public void restoreDataSource(JoinPoint point, DataSource dataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
System.out.println("Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]");
}
}
7.注解使用
比如在一个service实现类的方法上加注解@DataSource(“slave”),将数据源切换为slave数据源。那么这样做可能有一个弊端就是不够灵活,注解的这个方法中的所有数据源只能都是slave。
代码如下:
@DataSource("slave")
@Override
public List<Map> getAllTask() {
return taskMapper.getAllTask();
}
总结
本文主要是记录一下代码流程,如果想要更详细的流程解析,大家可以参考博客一。