Simple Spring Boot Multi-DataSource
// In Mapper@UsingDataSource(DataSourceType.SLAVE)
UserselectByPrimaryKey(Integerid);
// Or In Service@UsingDataSource(DataSourceType.MASTER)
publicUsergetUserFromSlave1(Integerid) {
returnuserDao.selectByPrimaryKey(id);
}
@UsingDataSource(DataSourceType.SLAVE)
publicUsergetUserFromSlave(Integerid) {
returnuserDao.selectByPrimaryKey(id);
}在 application.yml 中添加数据源对应的配置信息
spring: datasource: master: jdbc-url: jdbc:mysql://localhost:3306/multi_datasource_masterusername: rootpassword: rootrootslave: jdbc-url: jdbc:mysql://localhost:3306/multi_datasource_slaveusername: rootpassword: rootrootslave2: jdbc-url: jdbc:mysql://localhost:3306/multi_datasource_slave_2username: rootpassword: rootroot
数据源配置
@Configuration@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) publicclassDataSourceConfig { /** * 主库数据源配置,Bean name 与 {@link DataSourceType} 中对应枚举类型名相同,以达到校验作用 */@Bean("MASTER") @ConfigurationProperties(prefix = "spring.datasource.master") publicDataSourcemasterDataSource() { returnDataSourceBuilder.create().build(); } /** * 从库 1 数据源配置 */@Bean("SLAVE") @ConfigurationProperties(prefix = "spring.datasource.slave") publicDataSourceslaveDataSource() { returnDataSourceBuilder.create().build(); } /** * 从库 2 数据源配置 */@Bean("SLAVE_2") @ConfigurationProperties(prefix = "spring.datasource.slave2") publicDataSourceslave2DataSource() { returnDataSourceBuilder.create().build(); } }
定义数据源类型枚举
publicenumDataSourceType { /** * 主库 */MASTER, /** * 从库 1 */SLAVE, /** * 从库 2 */SLAVE_2, ; /** * 默认的数据源类型 */publicstaticfinalDataSourceTypeDEFAULT = MASTER; }
动态数据源配置,配合 UsingDataSource、UsingDataSourceAspect 实现数据源切换
@Primary@ComponentpublicclassDynamicRoutingDataSourceextendsAbstractRoutingDataSource { privatefinalMap<String, DataSource> dataSourceMap; publicstaticfinalThreadLocal<DataSourceType> TYPE_HOLDER = ThreadLocal.withInitial(() -> DataSourceType.DEFAULT); publicDynamicRoutingDataSource(Map<String, DataSource> dataSourceMap) { this.dataSourceMap = dataSourceMap; } @OverrideprotectedObjectdetermineCurrentLookupKey() { returnTYPE_HOLDER.get(); } @PostConstructprivatevoidpostConstruct() { // 设置默认数据源StringdefaultDataSourceName = DataSourceType.DEFAULT.name(); DataSourcedefaultDataSource = dataSourceMap.get(defaultDataSourceName); setDefaultTargetDataSource(defaultDataSource); // 设置数据源及其类型映射Map<Object, Object> targetDataSources = newHashMap<>(); dataSourceMap.forEach((name, ds) -> targetDataSources.put(DataSourceType.valueOf(name), ds)); setTargetDataSources(targetDataSources); } }
指定使用数据源类型注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UsingDataSource { DataSourceTypevalue() defaultDataSourceType.MASTER; }
数据源切换拦截切面
@Slf4j@Aspect@ComponentpublicclassUsingDataSourceAspect { @Around("@annotation(usingDataSource)") publicObjectaround(ProceedingJoinPointjoinPoint, UsingDataSourceusingDataSource) throwsThrowable { DataSourceTypetype = usingDataSource.value(); log.debug("data source type is {}", type); // 保存要切换到的数据源类型DynamicRoutingDataSource.TYPE_HOLDER.set(type); Objectres; try { res = joinPoint.proceed(); } finally { // 恢复数据源DynamicRoutingDataSource.TYPE_HOLDER.remove(); } returnres; } }
使用
@ServicepublicclassUserService { privatefinalUserDaouserDao; publicUserService(UserDaouserDao) { this.userDao = userDao; } publicUsergetUser(Integerid) { returnuserDao.selectByPrimaryKey(id); } @UsingDataSourcepublicUsergetUserFromDefault(Integerid) { returnuserDao.selectByPrimaryKey(id); } @UsingDataSource(DataSourceType.SLAVE) publicUsergetUserFromSlave1(Integerid) { returnuserDao.selectByPrimaryKey(id); } @UsingDataSource(DataSourceType.SLAVE_2) publicUsergetUserFromSlave2(Integerid) { returnuserDao.selectByPrimaryKey(id); } }
Have fun! 😁
你可以使用
MultiDatasourceApplicationTests.getUser()进行测试@SpringBootTestclassMultiDatasourceApplicationTests { @AutowiredprivateUserServiceuserService; @TestvoidcontextLoads() { } @TestpublicvoidgetUser() { Useruser; user = userService.getUser(1); System.out.println("user = " + user); user = userService.getUserFromDefault(1); System.out.println("user = " + user); user = userService.getUserFromSlave1(1); System.out.println("user = " + user); user = userService.getUserFromSlave2(1); System.out.println("user = " + user); user = userService.getUser(1); System.out.println("user = " + user); user = userService.getUserFromDefault(1); System.out.println("user = " + user); user = userService.getUserFromSlave1(1); System.out.println("user = " + user); user = userService.getUserFromSlave2(1); System.out.println("user = " + user); } }
user = User(id=1, username=master_username, password=master_password, sex=1, create_time=Fri Feb 19 11:59:35 CST 2021, update_time=Fri Feb 19 11:59:35 CST 2021, deleted=0) 2021-02-20 10:02:12.169 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is MASTER user = User(id=1, username=master_username, password=master_password, sex=1, create_time=Fri Feb 19 11:59:35 CST 2021, update_time=Fri Feb 19 11:59:35 CST 2021, deleted=0) 2021-02-20 10:02:12.171 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is SLAVE user = User(id=1, username=slave_username, password=slave_password, sex=1, create_time=Fri Feb 19 17:05:37 CST 2021, update_time=Fri Feb 19 17:05:37 CST 2021, deleted=0) 2021-02-20 10:02:12.186 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is SLAVE_2 user = User(id=1, username=slave_2_username, password=slave_2_password, sex=1, create_time=Sat Feb 20 10:00:59 CST 2021, update_time=Sat Feb 20 10:00:59 CST 2021, deleted=0) user = User(id=1, username=master_username, password=master_password, sex=1, create_time=Fri Feb 19 11:59:35 CST 2021, update_time=Fri Feb 19 11:59:35 CST 2021, deleted=0) 2021-02-20 10:02:12.203 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is MASTER user = User(id=1, username=master_username, password=master_password, sex=1, create_time=Fri Feb 19 11:59:35 CST 2021, update_time=Fri Feb 19 11:59:35 CST 2021, deleted=0) 2021-02-20 10:02:12.206 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is SLAVE user = User(id=1, username=slave_username, password=slave_password, sex=1, create_time=Fri Feb 19 17:05:37 CST 2021, update_time=Fri Feb 19 17:05:37 CST 2021, deleted=0) 2021-02-20 10:02:12.208 INFO 13053 --- [ main] c.e.m.config.UsingDataSourceAspect : data sourcetype is SLAVE_2 user = User(id=1, username=slave_2_username, password=slave_2_password, sex=1, create_time=Sat Feb 20 10:00:59 CST 2021, update_time=Sat Feb 20 10:00:59 CST 2021, deleted=0)