项目源码
spring-boot-dynamic-datasource-started-master.rar
多数据源配置
AbstractRoutingDataSource
spring jdbc包中,本身对多数据源的支持。
AbstractRoutingDataSource 是 DateSource 的子子类。
其中有targetDataSources 以map的形式包含所有注册的数据源。
实现该DataSource,并实现获取key的方法
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static final Logger LOG = Logger.getLogger(DynamicRoutingDataSource.class);
@Override
//SqlSessionFactory 拿用户指定的数据源的key
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.get();
}
}
配置文件把数据源的连接信息都写上
###spring boot自动配置单数据源###
spring.datasource.url=jdbc:mysql://localhost:3306/db_master?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=123456
###手动配置多数据源###
#mydb1
multiple.datasource.mydb1.driver-class-name=com.mysql.jdbc.Driver
multiple.datasource.mydb1.url=jdbc:mysql://localhost:3306/mydb1?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&allowMultiQueries=true
multiple.datasource.mydb1.username=root
multiple.datasource.mydb1.password=123456
#mydb2
multiple.datasource.mydb2.driver-class-name=com.mysql.jdbc.Driver
multiple.datasource.mydb2.url=jdbc:mysql://localhost:3306/mydb2?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&allowMultiQueries=true
multiple.datasource.mydb2.username=root
multiple.datasource.mydb2.password=123456
spring的配置类声明数据源
所有的数据源都放在了 DynamicRoutingDataSource的map中
这里dynamicDataSource 自己实现的DataSource 将给到需要数据源的地方使用
//使用 Druid 连接池管理, 手动配置前缀
@Bean
@ConfigurationProperties(prefix = "multiple.datasource.mydb1")
public DataSource mydb1() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "multiple.datasource.mydb2")
public DataSource mydb2() {
return DruidDataSourceBuilder.create().build();
}
/**
* 核心动态数据源
* @return 数据源实例
*/
@Bean
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
//默认 数据库
dataSource.setDefaultTargetDataSource(mydb1());
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DataSourceKey.mydb1, mydb1());
dataSourceMap.put(DataSourceKey.mydb2, mydb2());
dataSource.setTargetDataSources(dataSourceMap);
return dataSource;
}
给SqlSessionFactory 、 TransactionManager 用
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
//此处设置为了解决找不到mapper文件的问题
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
/**
* 事务管理
*
* @return 事务管理实例
*/
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
多数据源使用
思路:
配置已经完成,
现在需要在 sqlSessionFactroy 在生成 sqlSession之前,设置好我们指定的数据源。
生成 sqlSession 在 工厂内部会调用该方法,通过
AbstractRoutingDataSource determineCurrentLookupKey 拿到key, 再使用key去拿我们预先设置好的数据源。
使用注解标识事务方法,需要匹配的数据源
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
DataSourceKey dataSourceKey() default DataSourceKey.mydb1;
}
@Service("userInfoService")
public class UserInfoServiceImpl implements UserInfoService {
private static final Logger LOG = Logger.getLogger(UserInfoServiceImpl.class);
@Resource
private UserInfoMapper userInfoMapper;
@TargetDataSource(dataSourceKey = DataSourceKey.mydb1)
@Override
public List<UserInfo> listAll() {
return userInfoMapper.listAll();
}
@TargetDataSource(dataSourceKey = DataSourceKey.mydb1)
@Override
public int insert(UserInfo userInfo) {
return userInfoMapper.insert(userInfo);
}
@TargetDataSource(dataSourceKey = DataSourceKey.mydb2)
@Override
public List<UserInfo> listAlldb2() {
return userInfoMapper.listAll();
}
@TargetDataSource(dataSourceKey = DataSourceKey.mydb2)
@Override
public int insertdb2(UserInfo userInfo) {
return userInfoMapper.insert(userInfo);
}
}
使用aop在方法执行前,做数据源的设置。 数据源的设置放在ThreadLocal中
aop,切点为我们定义的注解。 往线程
@Before("@annotation(targetDataSource)")
public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
if (dataSourceKey == DataSourceKey.mydb1) {
DynamicDataSourceContextHolder.set(DataSourceKey.mydb1);
} if (dataSourceKey == DataSourceKey.mydb2) {
DynamicDataSourceContextHolder.set(DataSourceKey.mydb2);
}else{ //如果未匹配默认 mydb1
DynamicDataSourceContextHolder.set(DataSourceKey.mydb1);
}
}
使用ThreadLocal 存储设置(使用ThreadLocal好处:线程隔离,且配置暂存后本线程内可随处取出)
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<DataSourceKey> currentDatesource = new ThreadLocal<>();
public static void clear() {
currentDatesource.remove();
}
public static DataSourceKey get() {
return currentDatesource.get();
}
public static void set(DataSourceKey value) {
currentDatesource.set(value);
}
}
再回到自定义的数据源中看下。 从ThreadLocal中拿key,匹配上注解定义的数据源。
return DynamicDataSourceContextHolder.get();
实现只需在事务方法上定义注解,其余流程自动匹配数据源并执行返回