StringBoot 加入多个数据源进行动态切换
项目中需要多个数据源实现功能,看了网上的一些资料,最后整理了一份可行的代码,双数据源已经实现,如果需要多个数据源可直接添加扩展
application.properties配置文件添加数据源
spring.datasource1.driverClassName=oracle.jdbc.driver.OracleDriver
spring.datasource1.url=jdbc:oracle:thin:@//ip:1521/db
spring.datasource1.username=username
spring.datasource1.password=password
spring.datasource2.driverClassName=oracle.jdbc.driver.OracleDriver
spring.datasource2.url=jdbc:oracle:thin:@//ip:1521/db
spring.datasource2.username=username
spring.datasource2.password=password
…数据库剩余配置可以根据实际需求进行配置1234567891011
2.创建切换数据源类DatabaseContextHolder
/**
-
作用: 1、保存一个线程安全的DatabaseType容器,构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
*/
public class DatabaseContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();/*指定使用数据源/
public static void setDatabaseType(DatabaseType type) {
contextHolder.set(type);
}/*获取当前数据源/
public static DatabaseType getDatabaseType() {
return contextHolder.get();
}/*删除当前数据源/
public static void clearDatabaseType() {
contextHolder.remove();
}
}123456789101112131415161718192021
3.添加数据源名称DatabaseType ,此处最好跟数据库名称相同
public enum DatabaseType {
lotterytie, lottery
}123
4.设置动态数据源DynamicDataSource
/**
-
动态数据源(需要继承AbstractRoutingDataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}12345678910
5.创建数据源切换类MyBatisConfig
/*这里我用的是mybatis,在这里指定我的数据访问层/
@Configuration
@MapperScan(basePackages = “com.study.mapper”)
public class MyBatisConfig {
@Autowired
private Environment env;
/**
* 主数据源(默认使用)
*
* @return
* @throws Exception
*/
@Bean
public DataSource lotterytieDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("spring.datasource1.driverClassName"));
props.put("url", env.getProperty("spring.datasource1.url"));
props.put("username", env.getProperty("spring.datasource1.username"));
props.put("password", env.getProperty("spring.datasource1.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* 辅数据源
*
* @return
* @throws Exception
*/
@Bean
public DataSource lotteryDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("spring.datasource2.driverClassName"));
props.put("url", env.getProperty("spring.datasource2.url"));
props.put("username", env.getProperty("spring.datasource2.username"));
props.put("password", env.getProperty("spring.datasource2.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错(一般用于多数据源的情况下)
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("lotterytieDataSource") DataSource lotterytieDataSource,
@Qualifier("lotteryDataSource") DataSource lotteryDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.lotterytie, lotterytieDataSource);
targetDataSources.put(DatabaseType.lottery, lotteryDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(lotterytieDataSource);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("lotterytieDataSource") DataSource lotterytieDataSource,
@Qualifier("lotteryDataSource") DataSource lotteryDataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(lotterytieDataSource, lotteryDataSource));
fb.setTypeAliasesPackage(env.getProperty("com.study.mapper"));// 如使用注解方式,可以不用配置数据源
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
6.以上就可以切换数据源了
@Override
public void insertChangeRecordsEntity(Map<String, Object> param) {
/*在调用数据访问层时,指定使用哪个数据源/
// DatabaseContextHolder.setDatabaseType(DatabaseType.lottery);
lotterytieMapper.insertChangeRecordsEntity(param);
/**在当前数据源访问之后,需要删除辅助数据源,切回主数据源,如果自动切换,需要一定时间间隔,会造成数据访问层在访问其他数据源的时候,出现异常当前表不存在的问题,*/
// DatabaseContextHolder.clearDatabaseType();
// DatabaseContextHolder.setDatabaseType(DatabaseType.lotterytie);
}123456789
7.以上就可以实现动态切换数据源了,比较不方便的地方就是每一个需要切换数据源的service方法,都需要手动的指定一次,然后在转回主数据源或其他数据源,在这里我使用aop方式实现切换
在spring boot 的pom文件中添加aop依赖包
org.springframework.boot spring-boot-starter-aop 1234jar加好了就上代码:
/**
- 切换数据源时面向切面,使用完备用数据源,切换回主数据源
- @author Administrator
*/
@Aspect
@Component
public class LotteryAspect {
private static Logger logger = LoggerFactory.getLogger(LotteryAspect.class);
/**这里我在业务层指定路径,在该路径下所有的接口方法,在执行前都进行辅数据源的切换*/
@Pointcut("execution(public * com.study.service.lottery.*.*(..))")
public void lotteryDatasource() {
}
/**
* 执行当前service进行切面操作切换数据源
*
* @param joinPoint
* @throws Throwable
*/
@Before("lotteryDatasource()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
DatabaseContextHolder.setDatabaseType(DatabaseType.lottery);
}
/**
* 不管异常还是正常退出,执行的时候删除备用数据源切回主数据源
*
* @param jp
*/
@After("lotteryDatasource()")
public void after(JoinPoint jp) {
DatabaseContextHolder.clearDatabaseType();
DatabaseContextHolder.setDatabaseType(DatabaseType.lotterytie);
}
}
本文来自 不要忘记当初的目标 的优快云 博客 ,全文地址请点击:https://blog.youkuaiyun.com/jianshaoguang8886/article/details/81329658?utm_source=copy