关键词

SpringBoot多数据源读写分离的自定义配置问题及解决方法

  1. 背景介绍

Spring Boot 是一种基于 Spring 框架的快速开发 Web 应用的微服务框架,它的设计能够使开发者极速创建可独立运行的 Spring 应用程序。而在实际的开发过程中,很多业务场景需要使用多个数据源,并且多个数据源的读写分离也是一种非常常见的数据存储方案,这时候就需要对 Spring Boot 进行多数据源配置。

  1. Spring Boot 多数据源配置

在 Spring Boot 中实现多数据源配置可以通过以下步骤:

(1)引入相关依赖。

<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<!--Druid连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>

(2)在 application.yml 文件中进行数据源配置。

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave1:
      url: jdbc:mysql://localhost:3307/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

(3)自定义多数据源配置类。

@Configuration
public class DataSourceConfig {
    @Bean(name = "masterDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DruidDataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DruidDataSource slaveDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                               @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.getType(), masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE.getType(), slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource); //默认数据源
        dynamicDataSource.setTargetDataSources(targetDataSources);

        return dynamicDataSource;
    }
}

(4)自定义数据源持有类。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }
}

(5)定义数据源类型枚举类。

public enum DataSourceType {
    MASTER("master"),
    SLAVE("slave");

    private String type;

    DataSourceType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

(6)定义数据源 Holder 类,用于在线程中存储数据源类型的信息。

public class DataSourceHolder {
    /**
     * 使用ThreadLocal将数据源与当前线程绑定
     */
    private static final ThreadLocal<String> holder = new ThreadLocal<>();

    /**
     * 绑定数据源
     */
    public static void putDataSource(String dataSource) {
        holder.set(dataSource);
    }

    /**
     * 获取当前绑定的数据源
     */
    public static String getDataSource() {
        return holder.get();
    }

    /**
     * 清除绑定的数据源信息
     */
    public static void clear() {
        holder.remove();
    }
}

(7)在 Service 层的方法上加上数据源注解,注明该方法使用哪个数据源。

@Service
@Slf4j
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    /**
     * 使用主库进行写操作
     */
    @DataSource(value = DataSourceType.MASTER)
    @Override
    public int addUserInfo(UserInfo userInfo) {
        return userInfoMapper.insertSelective(userInfo);
    }

    /**
     * 使用从库进行读操作
     */
    @DataSource(value = DataSourceType.SLAVE)
    @Override
    public UserInfo getUserInfoById(String id) {
        return userInfoMapper.selectByPrimaryKey(id);
    }
}
  1. 示例说明

以下是两个示例,分别是对主库进行写操作和对从库进行读操作。

(1)向主库中插入一条用户信息。

@PostMapping("/addUser")
public String addUser(@RequestBody UserInfo userInfo) {
    try {
        userInfoService.addUserInfo(userInfo);
        return "添加成功!";
    } catch (Exception e) {
        e.printStackTrace();
        return "添加失败!";
    }
}

(2)从从库中查询一条用户信息。

@GetMapping("/getUser/{id}")
public UserInfo getUser(@PathVariable("id") String id) {
    try {
        return userInfoService.getUserInfoById(id);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
  1. 总结

通过以上步骤,我们即可轻松地实现 Spring Boot 的多数据源配置,实现读写分离,提高系统的并发能力和稳定性。需要注意的是,在实际的开发中,我们还需考虑到数据源的负载均衡、事务管理、多数据源切换等问题,需要在实际的生产环境中根据需求进行进一步的配置和调整。

本文链接:http://task.lmcjl.com/news/19024.html

展开阅读全文