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 1234

jar加好了就上代码:

/**

  • 切换数据源时面向切面,使用完备用数据源,切换回主数据源
  • @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);
}

}


本文来自 不要忘记当初的目标 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/jianshaoguang8886/article/details/81329658?utm_source=copy

10-04 19:51