赞
踩
仓库地址:https://gitee.com/aopmin/multi-datasource-demo
随着项目规模的扩大和业务需求的复杂化,单一数据源已经不能满足实际开发中的需求。在许多情况下,我们需要同时操作多个数据库,或者需要将不同类型的数据存储在不同的数据库中。这时,多数据源场景成为必不可少的解决方案。
市面上常见的多数据源实现方案如下:
方案1:基于Spring框架提供的AbstractRoutingDataSource。
方案2:使用MP提供的Dynamic-datasource多数据源框架。
方案3:通过自定义注解在方法或类上指定数据源,实现根据注解切换数据源的功能。
方案4:使用动态代理技术,在运行时动态切换数据源,实现多数据源的切换。
…
1、执行sql脚本:(分别创建两个数据库,里面都提供一张user表)
-- 创建数据库ds1 CREATE DATABASE `ds1`; -- 使用ds1数据库 USE ds1; -- 创建user表 CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键id', `username` VARCHAR(50) COMMENT '用户名', `gender` TINYINT(1) COMMENT '性别:0男,1女' ); -- 向user表插入数据 INSERT INTO user (username, gender) VALUES ('张三', 1), ('李四', 0), ('王五', 1); -- 创建数据库ds2 CREATE DATABASE `ds2`; -- 使用ds2数据库 USE ds2; -- 创建user表 CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键id', `username` VARCHAR(50) COMMENT '用户名', `gender` TINYINT(1) COMMENT '性别:0男,1女' ); -- 向user表插入数据 INSERT INTO user (username, gender) VALUES ('赵六', 1), ('陈七', 0), ('宝国', 1);
2、创建一个maven工程,向pom.xml中添加依赖:
<!--锁定SpringBoot版本--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.10</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <!--jdbc起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--test起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--jdbc起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies>
3、编写实体类:
package cn.aopmin.entity; import lombok.*; /** * 实体类 * * @author 白豆五 * @since 2024/7/4 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @ToString public class User { private Integer id; private String username; private Integer gender; }
4、创建application.yml文件,配置数据源:
spring: #动态数据源配置 datasource: ds1: driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/ds1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 ds2: driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/ds2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 logging: level: cn.aopmin: debug
5、编写数据源配置类:
package cn.aopmin.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 数据源配置类 * 配置多数据源和动态数据源 * * @author 白豆五 * @since 2024/7/4 */ @Configuration public class DataSourceConfig { //定义数据源1 @Bean("ds1") @ConfigurationProperties(prefix = "spring.datasource.ds1") public DataSource ds1() { return DataSourceBuilder.create().build(); } //定义数据源2 @Bean("ds2") @ConfigurationProperties(prefix = "spring.datasource.ds2") public DataSource ds2() { return DataSourceBuilder.create().build(); } //定义动态数据源 @Bean(name = "dataSource") public DataSource dynamicDataSource(@Qualifier("ds1") DataSource ds1, @Qualifier("ds2") DataSource ds2) { //1.定义数据源map Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("ds1", ds1); targetDataSources.put("ds2", ds2); //2.实例化自定义的DynamicDataSource对象, 并设置数据源map DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); //3.设置默认数据源,未匹配上则使用默认数据源 dynamicDataSource.setDefaultTargetDataSource(ds1); return dynamicDataSource; } // 通过JdbcTemplate @Bean public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource ds) { return new JdbcTemplate(ds); } }
6、创建DynamicDataSource动态数据类:
package cn.aopmin.config; import cn.aopmin.common.DataSourceContextHolder; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * AbstractRoutingDataSource(抽象的数据源路由器) 的基本原理是, 它维护了一个数据源的集合,每个数据源都有唯一的一个标识符 * 当应用程序需要访问数据库的时候,AbstractRoutingDataSource会根据某种匹配规则(例如请求参数、用户身份等)来选择一个合适的数据源, * 并将请求转发给这个数据源。 */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 获取数据源名称 * @return */ @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }
7、定义一个ThreadLocal工具类:
package cn.aopmin.common; /** * 使用ThreadLocal保存数据源名称 * * @author 白豆五 * @since 2024/7/4 */ public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 将数据源名称绑定到当前线程上 public static void setDataSource(String dataSourceName) { contextHolder.set(dataSourceName); } // 获取当前线程上的数据源名称 public static String getDataSource() { return contextHolder.get(); } // 清除数据源名称 public static void clearDataSource() { contextHolder.remove(); } }
8、创建启动类
package cn.aopmin; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 启动类 * * @author 白豆五 * @since 2024/7/3 */ @SpringBootApplication public class Demo01Application { public static void main(String[] args) { SpringApplication.run(Demo01Application.class, args); } }
9、创建UserService:
package cn.aopmin.service; import cn.aopmin.common.DataSourceContextHolder; import cn.aopmin.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** * @author 白豆五 * @since 2024/7/4 */ @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public void insertDs1(User user) { try { // todo:自定义注解+SpringAop实现数据源的切换 DataSourceContextHolder.setDataSource("ds1"); String sql = "insert into user(username,gender) values(?,?)"; jdbcTemplate.update(sql,user.getUsername(), user.getGender()); } finally { DataSourceContextHolder.clearDataSource(); } } public void insertDs2(User user) { try { DataSourceContextHolder.setDataSource("ds2"); String sql = "insert into user(username,gender) values(?,?)"; jdbcTemplate.update(sql,user.getUsername(), user.getGender()); } finally { DataSourceContextHolder.clearDataSource(); } } }
10、编写测试:
package cn.aopmin.service; import cn.aopmin.Demo01Application; import cn.aopmin.entity.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; @SpringBootTest(classes = {Demo01Application.class}) public class UserServiceTest { @Resource private UserService userService; @Test public void testInsertDs1() { User user = new User(); user.setUsername("jack"); user.setGender(0); user.setGender(1); userService.insertDs1(user); } @Test public void testInsertDs2() { User user = new User(); user.setUsername("rose"); user.setGender(1); userService.insertDs2(user); } }
最终效果:
mp文档:https://baomidou.com/guides/dynamic-datasource/#_top
1、创建SpringBoot工程,引入Dynamic-datasource依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
2、配置数据源:
spring: #多数据源配置 datasource: dynamic: primary: master #设置默认数据源 strict: false #是否严格检查动态数据源提供的数据库名 datasource: #数据源1 master: url: jdbc:mysql:///ds1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver #数据源2 slave1: url: jdbc:mysql:///ds2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
3、实体类:
package cn.aopmin.entity; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * 实体类 * * @author 白豆五 * @since 2024/7/4 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String username; private Integer gender; }
4、业务类:
package cn.aopmin.service; import cn.aopmin.entity.User; import com.baomidou.dynamic.datasource.annotation.DS; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** * 通过@DS注解切换数据源 * @author 白豆五 * @since 2024/7/4 */ @Service // @DS("master") //不加@DS注解,会使用默认数据源 public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public void insertDs1(User user) { String sql = "insert into user(username,gender) values(?,?)"; jdbcTemplate.update(sql,user.getUsername(), user.getGender()); } @DS("slave1") public void insertDs2(User user) { String sql = "insert into user(username,gender) values(?,?)"; jdbcTemplate.update(sql,user.getUsername(), user.getGender()); } }
4、测试类:
package cn.aopmin.service; import cn.aopmin.entity.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; @SpringBootTest public class UserServiceTest2 { @Resource private UserService userService; @Test public void testInsertDs1() { User user = new User(); user.setUsername("jack"); user.setGender(0); user.setGender(1); userService.insertDs1(user); } @Test public void testInsertDs2() { User user = new User(); user.setUsername("rose"); user.setGender(1); userService.insertDs2(user); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。