【多数据源动态切换】Spring Boot中实现多数据源动态切换效果(2):通过开源项目Dynamic Datasource Spring Boot Starter实现

人生之路不会是一帆风顺的,我们会遇上顺境,也会遇上逆境,在所有成功路上折磨你的,背后都隐藏着激励你奋发向上的动机,人生没有如果,只有后果与结果,成熟,就是用微笑来面对一切小事。

导读:本篇文章讲解 【多数据源动态切换】Spring Boot中实现多数据源动态切换效果(2):通过开源项目Dynamic Datasource Spring Boot Starter实现,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

在Spring Boot中,可以通过多种方式实现多数据源的动态切换效果,本篇介绍第二种实现方案。

一 具体实现

(1)测试使用的数据库

这里我们创建3个数据库,分别是:db01db02db03,然后这3个数据库都有一张名为user_info的表,表结构一样,只是数据不同。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

— 建表语句

DROP TABLE IF EXISTS `user_info`;

CREATE TABLE `user_info` (

  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘ID’,

  `name` varchar(255) DEFAULT NULL COMMENT ‘姓名’,

  `age` int(11) DEFAULT NULL COMMENT ‘年龄’,

  `addr_city` varchar(255) DEFAULT NULL COMMENT ‘所在城市’,

  `addr_district` varchar(255) DEFAULT NULL COMMENT ‘所在区’,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

— db01中表「user_info」的数据

INSERT INTO `user_info` VALUES (‘1’, ‘张三’, ’20’, ‘北京’, ‘朝阳区’);

INSERT INTO `user_info` VALUES (‘2’, ‘李四’, ’18’, ‘北京’, ‘东城区’);

— db02中表「user_info」的数据

INSERT INTO `user_info` VALUES (‘1’, ‘王五’, ’22’, ‘上海’, ‘普陀区’);

INSERT INTO `user_info` VALUES (‘2’, ‘赵六’, ’24’, ‘上海’, ‘浦东新区’);

— db03中表「user_info」的数据

INSERT INTO `user_info` VALUES (‘1’, ‘孙七’, ’28’, ‘成都’, ‘武侯区’);

INSERT INTO `user_info` VALUES (‘2’, ‘周八’, ’26’, ‘成都’, ‘天府新区’);

(2)在pom.xml文件中添加相关依赖

1

2

3

4

5

<dependency>

    <groupId>com.baomidou</groupId>

    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>

    <version>3.2.1</version>

</dependency>

最新版本:https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter

(3)新增application-datasource2.yml配置文件

新建这个用于测试的配置文件,主要配置了接下来需要用到的多个数据源,其配置如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

server:

  port: 8080

  servlet:

    session.timeout: 300

logging:

  level:

    org.springframework.web: debug

    cn.zifangsky: debug

  file:

    name: web-exercise.log

    path: logs

spring:

  datasource:

    # HikariCP 连接池配置

    hikari:

      pool-name: exercise_HikariCP

      minimum-idle: 5  #最小空闲连接数量

      idle-timeout: 30000  #空闲连接存活最大时间,默认600000(10分钟)

      maximum-pool-size: 20  #连接池最大连接数,默认是10

      auto-commit: true  #此属性控制从池返回的连接的默认自动提交行为,默认值:true

      max-lifetime: 1800000  #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟

      connection-timeout: 30000  #数据库连接超时时间,默认30秒,即30000

    dynamic:

      primary: db01  #设置默认的数据源或者数据源组,默认值为master

      datasource:

        db01:

          type: com.zaxxer.hikari.HikariDataSource

          driver-class-name: com.mysql.jdbc.Driver

          url: jdbc:mysql://localhost:3306/db01?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&failOverReadOnly=false&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai

          username: root

          password: root

        db02:

          type: com.zaxxer.hikari.HikariDataSource

          driver-class-name: com.mysql.jdbc.Driver

          url: jdbc:mysql://localhost:3306/db02?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&failOverReadOnly=false&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai

          username: root

          password: root

        db03:

          type: com.zaxxer.hikari.HikariDataSource

          driver-class-name: com.mysql.jdbc.Driver

          url: jdbc:mysql://localhost:3306/db03?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&failOverReadOnly=false&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai

          username: root

          password: root

#mybatis

mybatis:

  type-aliases-package: cn.zifangsky.example.webexercise.mapper

  mapper-locations: classpath:mapper/*.xml

(4)新建一个测试使用的Mapper

上篇文章的基础上,再新建一个测试使用的Mapper,跟上篇文章的那个Mapper类似,只是使用的注解不同而已。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

package cn.zifangsky.example.webexercise.mapper;

import cn.zifangsky.example.webexercise.model.UserInfo;

import com.baomidou.dynamic.datasource.annotation.DS;

import org.apache.ibatis.annotations.Param;

@DS(“db02”)

public interface UserInfoDynamicMapper2 {

    /**

     * 通过默认数据源查询,方法级别的注解优先级更高

     */

    @DS(“db01”)

    UserInfo selectByDefaultDataSource(Integer id);

    /**

     * 方法级别没有添加注解,则使用接口级别的注解,通过 db02 数据源查询

     */

    UserInfo selectByDB02DataSource(Integer id);

    /**

     * 通过 db03 数据源查询

     */

    @DS(“db03”)

    UserInfo selectByDB03DataSource(Integer id);

    /**

     * 测试事务是否回滚(数据插入 db02 数据源)

     */

    @DS(“db02”)

    int addToDB02(UserInfo record);

    /**

     * 测试事务是否回滚(数据插入 db03 数据源)

     */

    @DS(“db03”)

    int addToDB03(UserInfo record);

    /**

     * 从 db03 数据源删除数据

     */

    @DS(“db03”)

    int deleteFromDB03ByName(@Param(“name”) String name);

}

其对应的UserInfoDynamicMapper.xml文件(文件内容除了类路径不同,其他跟上篇文章的那个Mapper.xml一样)是:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “http://mybatis.org/dtd/mybatis-3-mapper.dtd”>

<mapper namespace=”cn.zifangsky.example.webexercise.mapper.UserInfoDynamicMapper2″>

  <resultMap id=”BaseResultMap” type=”cn.zifangsky.example.webexercise.model.UserInfo”>

    <id column=”id” jdbcType=”INTEGER” property=”id” />

    <result column=”name” jdbcType=”VARCHAR” property=”name” />

    <result column=”age” jdbcType=”INTEGER” property=”age” />

    <result column=”addr_city” jdbcType=”VARCHAR” property=”addrCity” />

    <result column=”addr_district” jdbcType=”VARCHAR” property=”addrDistrict” />

  </resultMap>

  <sql id=”Base_Column_List”>

    id, `name`, age, addr_city, addr_district

  </sql>

  <select id=”selectByDefaultDataSource” parameterType=”java.lang.Integer” resultMap=”BaseResultMap”>

    select

    <include refid=”Base_Column_List” />

    from user_info

    where id = #{id,jdbcType=INTEGER}

  </select>

  <select id=”selectByDB02DataSource” parameterType=”java.lang.Integer” resultMap=”BaseResultMap”>

    select

    <include refid=”Base_Column_List” />

    from user_info

    where id = #{id,jdbcType=INTEGER}

  </select>

  <select id=”selectByDB03DataSource” parameterType=”java.lang.Integer” resultMap=”BaseResultMap”>

    select

    <include refid=”Base_Column_List” />

    from user_info

    where id = #{id,jdbcType=INTEGER}

  </select>

  <insert id=”addToDB02″ keyColumn=”id” keyProperty=”id” parameterType=”cn.zifangsky.example.webexercise.model.UserInfo” useGeneratedKeys=”true”>

    insert into user_info (`name`, age, addr_city,

      addr_district)

    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{addrCity,jdbcType=VARCHAR},

      #{addrDistrict,jdbcType=VARCHAR})

  </insert>

  <insert id=”addToDB03″ keyColumn=”id” keyProperty=”id” parameterType=”cn.zifangsky.example.webexercise.model.UserInfo” useGeneratedKeys=”true”>

    insert into user_info (`name`, age, addr_city,

      addr_district)

    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{addrCity,jdbcType=VARCHAR},

      #{addrDistrict,jdbcType=VARCHAR})

  </insert>

  <delete id=”deleteFromDB03ByName” parameterType=”java.lang.String”>

    delete from user_info

    where name = #{name,jdbcType=VARCHAR}

  </delete>

</mapper>

(5)使用单元测试测试「动态切换数据源」的效果

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

package cn.zifangsky.example.webexercise.dataSource;

import cn.zifangsky.example.webexercise.mapper.UserInfoDynamicMapper2;

import cn.zifangsky.example.webexercise.mapper.UserInfoMapper;

import cn.zifangsky.example.webexercise.model.UserInfo;

import org.junit.jupiter.api.*;

import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit.jupiter.SpringExtension;

import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;

/**

* 测试动态切换数据源(Dynamic Datasource Spring Boot Starter)

*

*/

@DisplayName(“测试动态切换数据源(Dynamic Datasource Spring Boot Starter)”)

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

@ExtendWith(SpringExtension.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class DynamicDataSource2Test {

    @Autowired

    private UserInfoMapper userInfoMapper;

    @Autowired

    private UserInfoDynamicMapper2 userInfoDynamicMapper2;

    @Test

    @Order(1)

    @DisplayName(“普通方法——使用默认数据源”)

    public void testCommonMethod(){

        UserInfo userInfo = userInfoMapper.selectByPrimaryKey(1);

        Assertions.assertNotNull(userInfo);

        Assertions.assertEquals(“张三”, userInfo.getName());

    }

    @Test

    @Order(2)

    @DisplayName(“通过默认数据源查询,方法级别的注解优先级更高”)

    public void testSelectByDefaultDataSource(){

        UserInfo userInfo = userInfoDynamicMapper2.selectByDefaultDataSource(1);

        Assertions.assertNotNull(userInfo);

        Assertions.assertEquals(“张三”, userInfo.getName());

    }

    @Test

    @Order(3)

    @DisplayName(“方法级别没有添加注解,则使用接口级别的注解,通过 db02 数据源查询”)

    public void testSelectByDB02DataSource(){

        UserInfo userInfo = userInfoDynamicMapper2.selectByDB02DataSource(1);

        Assertions.assertNotNull(userInfo);

        Assertions.assertEquals(“王五”, userInfo.getName());

    }

    @Test

    @Order(4)

    @DisplayName(“方法级别添加注解,手动指定通过 db03 数据源查询”)

    public void testSelectByDB03DataSource(){

        UserInfo userInfo = userInfoDynamicMapper2.selectByDB03DataSource(1);

        Assertions.assertNotNull(userInfo);

        Assertions.assertEquals(“孙七”, userInfo.getName());

    }

    @Test

    @Order(5)

    @DisplayName(“在一个方法执行过程中嵌套操作多个数据源的情况”)

    public void testNestedMultiDataSource(){

        //1. 从 db02 查询一条数据

        UserInfo userInfo = userInfoDynamicMapper2.selectByDB02DataSource(1);

        //2. 插入到 db03

        userInfo.setId(null);

        userInfoDynamicMapper2.addToDB03(userInfo);

    }

    @Test

    @Order(6)

    @DisplayName(“从 db03 数据源删除数据”)

    public void testDeleteFromDB03ByName(){

        userInfoDynamicMapper2.deleteFromDB03ByName(“王五”);

    }

    @Test

    @Order(7)

    @DisplayName(“嵌套多个数据源的事务回滚情况”)

    @Transactional(rollbackFor = Exception.class)

    public void testTransaction() throws SQLException {

        //1. 从 db01 查询一条数据

        UserInfo userInfo = userInfoDynamicMapper2.selectByDefaultDataSource(1);

        //2. 分别插入到 db02 和 db03

        userInfo.setId(null);

        userInfoDynamicMapper2.addToDB02(userInfo);

        userInfoDynamicMapper2.addToDB03(userInfo);

        //3. 手动抛出一个异常,测试事务回滚效果

        throw new SQLException(“SQL执行过程中发生某些未知异常”);

    }

}

注:以上测试代码基于Junit5 测试框架编写,需要的依赖如下:

1

2

3

4

5

6

7

8

9

10

11

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>

    <scope>test</scope>

    <exclusions>

        <exclusion>

            <groupId>org.junit.vintage</groupId>

            <artifactId>junit-vintage-engine</artifactId>

        </exclusion>

    </exclusions>

</dependency>

运行单元测试后,其测试结果跟上篇文章一样,这里就省略截图吧。

二 这两种方案如何选择

我看了一下开源项目Dynamic Datasource Spring Boot Starter的源代码,发现它也有一个DynamicRoutingDataSource(com/baomidou/dynamic/datasource/DynamicRoutingDataSource.java),然后具体的实现逻辑跟我在上篇文章中介绍的那种方案实际也是类似的。

【多数据源动态切换】Spring Boot中实现多数据源动态切换效果(2):通过开源项目Dynamic Datasource Spring Boot Starter实现 

不过,通过查看这个开源项目的官方文档可以得知,这个项目支持的特性比较丰富,截止目前有以下这些:

【多数据源动态切换】Spring Boot中实现多数据源动态切换效果(2):通过开源项目Dynamic Datasource Spring Boot Starter实现

然后,经过了多次更新迭代后,这个开源项目也相对比较稳定。因此,在这里我给出的建议是:

  • 如果想要实现简单,或者说想要将以上截图中的部分特性拿来就用,那么可以考虑使用这个开源项目;

  • 如果想要实现的功能比较单一,而且有尽可能减少外部依赖的需求,那么通过上篇文章介绍的方案来手动实现也是可以的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/124468.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!