SpringBoot:基于SpringBoot实现多数据源动态切换

一、前言

  Spring Boot 多数据源,也称为动态数据源,是指在Spring Boot项目中配置多个数据源,以支持不同的数据库访问需求。这种配置方式在项目开发逐渐扩大、单个数据源无法满足项目支撑需求时变得尤为重要。

二、目的

配置多数据源的主要目的是为了满足复杂的业务需求,例如:

  ①. 当一个系统需要集成多个项目时,配置多数据源可以方便地管理不同项目的数据。

  ②. 当系统/服务需要对接多个系统,存入多个不同的表时,多数据源配置可以确保数据的安全性和隔离性。

  ③. 在读写分离的场景中,主库负责增删改操作,从库负责查询操作,通过多数据源配置可以提高系统的性能和稳定性。

三、实现原理

源码展示
在这里插入图片描述
  就是通过determineTargetDataSource中的determineCurrentLookupKey,它是个抽象方法,我们可以继承这个抽象类,去实现多数据源的切换。
在这里插入图片描述

三、代码示例

1. 在application.properties中配置多个数据源的数据库

datasource.master.type=com.alibaba.druid.pool.DruidDataSource
datasource.master.driver-class-name=com.mysql.jdbc.Driver
datasource.master.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysql?relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull&characterEncoding=utf-8&useSSL=false
datasource.master.username=root
datasource.master.password=root


datasource.slave.type=com.alibaba.druid.pool.DruidDataSource
datasource.slave.driver-class-name=com.mysql.jdbc.Driver
datasource.slave.jdbc-url=jdbc:mysql://127.0.0.1:3306/mysql2?relaxAutoCommit=true&zeroDateTimeBehavior=convertToNull&characterEncoding=utf-8&useSSL=false
datasource.slave.username=root
datasource.slave.password=root

dataSourceConfig.use=true

2. 定义数据源枚举类

package com.example.yddemo.RoutingData;

public enum DataSourceType {

    MASTER("master"), SLAVE("slave");

    String value;

    private DataSourceType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

3. 定义一个配置类,加载我们的自定义属性值

@Configuration
@ConditionalOnProperty(name = "dataSourceConfig.use", havingValue = "true")
public class DataSourceConfig {

    @Bean("masterDatasource")
    @ConfigurationProperties(prefix = "datasource.master")
    public DataSource defaultDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("slaveDatasource")
    @ConfigurationProperties(prefix = "datasource.slave")
    public DataSource slaveDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean
    @DependsOn({"masterDatasource", "slaveDatasource"})
    @Primary
    public DynamicDataSource dataSource(DataSource masterDatasource, DataSource slaveDatasource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.value, masterDatasource);
        targetDataSources.put(DataSourceType.SLAVE.value, slaveDatasource);
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(masterDatasource);
        return dataSource;
    }

    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

4. 定义数据源切换处理类

定义一个类让它继承AbstractRoutingDataSource,并重写determineCurrentLookupKey

public class DynamicDataSource extends AbstractRoutingDataSource {

    //用来保存数据源与获取数据源
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public DynamicDataSource() {
    }

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(new HashMap<Object, Object>(targetDataSources));
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    /**
     * 创建切换数据源处理类
     * @param dataSource
     */
    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

5. 为了方便使用,我们自定义注解

@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    String name() default "";

}

6. 使用AOP,让我们的自定义注解生效

@Aspect
@Component
public class DataSourceAspect implements Ordered {

    @Pointcut("@annotation(com.example.yddemo.RoutingData.DataSource)")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        try {
            DataSource dataSource = method.getAnnotation(DataSource.class);
            if (dataSource == null) {
                DynamicDataSource.setDataSource(DataSourceType.MASTER.getValue());
            } else {
                DynamicDataSource.setDataSource(dataSource.name());
            }
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }

}

7. 定义service方法测试,使用自定义注解

实体类

实体类
public class User {

    private Integer id;

    private String name;

    private String phone;

    private String email;
    
    // ... get  set
}

Mapper接口

@Mapper
public interface UserMapper {

    User getUserInfo(Integer id);

}

UserMapper.xml

<mapper namespace="com.example.yddemo.mapper.UserMapper" >

  <select id="getUserInfo" resultType="com.example.yddemo.RoutingData.User" >
    select
    id,name,phone,email
    from sys_user where id = #{id}
  </select>
  
</mapper>

Service实现类

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserInfo(Integer id) {
        User userInfo = userMapper.getUserInfo(id);
        return userInfo;
    }

    @Override
    @DataSource(name = "slave")
    public User getSlaveUserInfo(Integer id) {
        return userMapper.getUserInfo(id);
    }
}

8. Controller里面进行方法调用

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/query")
    public User userQuery(Integer id) {
        // 业务逻辑
        return userService.getUserInfo(id);
    }


    @GetMapping("/user/slave/query")
    public User userQuery1(Integer id) {
        // 业务逻辑
        return userService.getSlaveUserInfo(id);
    }

}

注意:启动类上面加上@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}), 关闭 spring 的自动数据源配置

9. 运行结果
在这里插入图片描述

从库数据展示在这里插入图片描述

  以上就是关于多数据源切换的全部内容,有AOP,自定义注解,自定义属性配置等方面的知识点。希望对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值