Java实现动态切换数据源

本文介绍了如何在Spring项目中使用AbstractRoutingDataSource动态管理多个数据库连接,根据登录账号信息自动切换数据源,通过Redis存储数据库配置。实例演示了如何设置数据源和测试方法,适用于分库部署的系统需求。

在一般的Java项目中,如果使用Spring去管理数据库连接信息,一般只能连接一个数据库,可是会有部分情况我们需要连接多个数据库,甚至还会存在不同的请求需要根据配置信息连接不同的数据库,比如:

在很多sass系统中,是采用分库部署的方式,就是不同账号连接的是不同的数据库,可是服务只有一个,所以就需要根据登录的账号信息动态切换数据源。

下面给出一个简单的demo,如果有需要大家可以根据自己的情况去改写。

我们知道,spring在执行sql的时候,是要先获取DataSource对象,然后getConnection获取连接,然后再执行excuteSql去真正执行sql语句。所以在sql执行之前将DataSource对象替换掉,就能动态切换数据源。

这里我们需要用到抽象类AbstractRoutingDataSource,我们需要继承这个类,并且重写determineTargetDataSource方法,这个方法会返回DataSource对象。

@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
    public final static String JDBC_CONNECT_PARAM = "?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true";

    static RedissonClient redissonClient;
    @Autowired
    public void setRedisCache(RedissonClient redissonClient) {
        DynamicDataSource.redissonClient = redissonClient;
    }

    /**
     * 数据源MAP
     */
    public stati
Java 应用中,使用 **MyBatis 实现动态切换数据源** 是一个常见的需求,尤其是在多租户系统、读写分离、分库分表等场景中。 --- ## ✅ 实现方式 要实现动态切换数据源,通常需要以下几个组件: 1. **多个数据源(DataSource)** 2. **动态数据源路由(AbstractRoutingDataSource)** 3. **ThreadLocal 保存当前线程的数据源标识** 4. **MyBatis 整合 Spring 管理数据源** 下面是一个完整的实现示例,使用 **Spring + MyBatis + AbstractRoutingDataSource** 实现动态切换数据源。 --- ## ✅ 示例代码 ### 1. 配置多个数据源(applicationContext.xml 或 Java Config) #### 使用 Java 配置类方式: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfig { @Bean public DataSource dataSource1() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/db1") .username("root") .password("123456") .driverClassName("com.mysql.cj.jdbc.Driver") .build(); } @Bean public DataSource dataSource2() { return DataSourceBuilder.create() .url("jdbc:mysql://localhost:3306/db2") .username("root") .password("123456") .driverClassName("com.mysql.cj.jdbc.Driver") .build(); } @Bean public DataSource dynamicDataSource(DataSource dataSource1, DataSource dataSource2) { AbstractRoutingDataSource ds = new AbstractRoutingDataSource() { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }; Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("db1", dataSource1); targetDataSources.put("db2", dataSource2); ds.setTargetDataSources(targetDataSources); ds.setDefaultTargetDataSource(dataSource1); // 默认数据源 return ds; } @Bean public DataSourceTransactionManager transactionManager(DataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } } ``` --- ### 2. 定义数据源上下文(使用 ThreadLocal) ```java public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } } ``` --- ### 3. 自定义注解 + AOP 实现自动切换数据源(可选) #### 定义注解: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TargetDataSource { String value(); } ``` #### AOP 切面: ```java @Aspect @Component public class DataSourceAspect { @Before("@annotation(targetDataSource)") public void before(JoinPoint point, TargetDataSource targetDataSource) { DataSourceContextHolder.setDataSource(targetDataSource.value()); } @After("@annotation(targetDataSource)") public void after(JoinPoint point, TargetDataSource targetDataSource) { DataSourceContextHolder.clearDataSource(); } } ``` --- ### 4. 使用示例:Mapper + Service #### Mapper 接口: ```java @Mapper public interface UserMapper { List<User> selectAll(); } ``` #### Service: ```java @Service public class UserService { @Autowired private UserMapper userMapper; @TargetDataSource("db1") public List<User> getUsersFromDb1() { return userMapper.selectAll(); } @TargetDataSource("db2") public List<User> getUsersFromDb2() { return userMapper.selectAll(); } } ``` --- ## ✅ 总结 通过继承 `AbstractRoutingDataSource` 并结合 `ThreadLocal`,我们可以实现动态切换数据源。结合 Spring AOP,还可以实现注解方式的自动切换,非常适用于多数据源场景。 --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值