在开发中,动态数据源配置还是用的比较多的,比如在多数据源使用方面,又或者是在多个DB之间切换方面。这里给出一个动态数据源的配置方案,两个DB均以mysql为例。
多数据源配置引入
mybatis和mysql在springboot中的引入这里就不在说了,不了解的可以参见springboot中mysql与mybatis的引入。
数据源配置如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | datasource:master:
 type: com.alibaba.druid.pool.DruidDataSource
 jdbc-url: jdbc:mysql://127.0.0.1:3306/sbac_master?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
 username: root
 password: 1234
 driver-class-name: com.mysql.cj.jdbc.Driver
 log:
 type: com.alibaba.druid.pool.DruidDataSource
 jdbc-url: jdbc:mysql://127.0.0.1:3306/sbac_log?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
 username: root
 password: 1234
 driver-class-name: com.mysql.cj.jdbc.Driver
 
 | 
mybatis的配置引入如下:
| 12
 3
 
 | mybatis:config-location: classpath:mybatis-config.xml
 mapper-locations: classpath:com/lazycece/sbac/mysql/data/dao/*/mapper/*.xml
 
 | 
这里已然使用的是springboot的自动配置功能配置mybatis信息,只是手动指定了数据源的。如下所示,指定了master和log两个数据源,设置master为默认数据源:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | @Configurationpublic class MultiDataSource {
 
 public static final String MASTER_DATA_SOURCE = "masterDataSource";
 public static final String LOG_DATA_SOURCE = "logDataSource";
 
 @Bean(name = MultiDataSource.MASTER_DATA_SOURCE)
 @ConfigurationProperties(prefix = "datasource.master")
 public DataSource masterDataSource() {
 return DataSourceBuilder.create().build();
 }
 
 @Bean(name = MultiDataSource.LOG_DATA_SOURCE)
 @ConfigurationProperties(prefix = "datasource.log")
 public DataSource logDataSource() {
 return DataSourceBuilder.create().build();
 }
 
 @Primary
 @Bean(name = "dynamicDataSource")
 public DynamicDataSource dataSource() {
 DynamicDataSource dynamicDataSource = new DynamicDataSource();
 dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
 Map<Object, Object> dataSourceMap = new HashMap<>(4);
 dataSourceMap.put(MASTER_DATA_SOURCE, masterDataSource());
 dataSourceMap.put(LOG_DATA_SOURCE, logDataSource());
 dynamicDataSource.setTargetDataSources(dataSourceMap);
 return dynamicDataSource;
 }
 }
 
 | 
动态数据源路由实现
引入了配置信息之后,便是该说如何实现多数据源切换了。我们是通过实现AbstractRoutingDataSource类的determineCurrentLookupKey方法来实现数据源的动态路由,设置ThreadLocal线程保护变量存储数据源key,确保线程间不受影响。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | package com.lazycece.sbac.mysql.multi.config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
 
 
 
 public class DynamicDataSource extends AbstractRoutingDataSource {
 private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
 
 private static final ThreadLocal<String> DATA_SOURCE_KEY = new ThreadLocal<>();
 
 static void changeDataSource(String dataSourceKey) {
 DATA_SOURCE_KEY.set(dataSourceKey);
 }
 
 static void clearDataSource() {
 DATA_SOURCE_KEY.remove();
 }
 
 @Override
 protected Object determineCurrentLookupKey() {
 String key = DATA_SOURCE_KEY.get();
 LOGGER.info("current data-source is {}", key);
 return key;
 }
 }
 
 | 
随后,便是用AOP的方式来实现数据源的动态切换,注解和切面定义如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | @Documented@Inherited
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface DataSource {
 
 String value();
 }
 
 @Component
 @Aspect
 public class DataSourceConfig {
 
 @Before("@annotation(dataSource)")
 public void beforeSwitchDataSource(DataSource dataSource) {
 DynamicDataSource.changeDataSource(dataSource.value());
 }
 
 @After("@annotation(DataSource)")
 public void afterSwitchDataSource() {
 DynamicDataSource.clearDataSource();
 }
 }
 
 | 
动态数据源切换使用
动态数据源切换只需要在业务中使用@DataSource注解来标明需要使用的数据源即可,如下所示(这里只贴出关键代码):
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | @Servicepublic class DynamicDataSourceServiceImpl implements DynamicDataSourceService {
 
 @Resource
 private UserDao userDao;
 @Resource
 private SystemLogDao systemLogDao;
 
 @Override
 @DataSource(value = MultiDataSource.MASTER_DATA_SOURCE)
 public void addUserInfo(User user) {
 userDao.insert(user);
 }
 
 @Override
 @DataSource(value = MultiDataSource.MASTER_DATA_SOURCE)
 public User getUserInfo(String username) {
 return userDao.findByUsername(username);
 }
 
 @Override
 @DataSource(value = MultiDataSource.LOG_DATA_SOURCE)
 public void addSystemLog(SystemLog systemLog) {
 systemLogDao.insert(systemLog);
 }
 
 @Override
 @DataSource(value = MultiDataSource.LOG_DATA_SOURCE)
 public List<SystemLog> getSystemLogInfo(Date beginTime, Date endTime) {
 return systemLogDao.findByCreateTime(beginTime, endTime);
 }
 }
 
 | 
案例源码
案例源码地址: https://github.com/lazycece/springboot-actual-combat/tree/master/springboot-ac-mysql/springboot-ac-mysql-multi