简单几步学会使用多数据源

简单几步学会使用多数据源

前言

我们在日常的工作中肯定会遇到一种情况,一个服务的数据存在数据库A,另一个服务的数据存在数据库B,而我们需要去对这两个服务的数据进行整合,那么除了微服务之外我们还有一种实现思路:多数据源。

简介

dynamic-datasource 是一款开箱即用的轻量级开源框架,主要用于解决动态数据源切换的问题。该框架使用简单,支持多种数据库类型,可以按需配置数据源,只需要简单的几个步骤就可以实现多数据源动态切换。

使用

引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.19</version>
</dependency>

配置

在这里为了演示就指定到同一个数据源下的不同数据库,我们可以创建一个单独的配置文件来管理我们的数据源,接着我们建立一个 application-druid.yml 的配置文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
spring:
datasource:
dynamic:
# 指定主数据源
primary: user
# 是否启用数据源严格匹配,默认为false,使用默认数据源
# 当其为true时未匹配到指定数据源会报错
strict: false
# 懒加载
lazy: true
datasource:
# 主数据源配置
master_1:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///user?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
username: root
password: root
# 从数据源配置
slave_1:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///order?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
username: root
password: root

之后我们再在 application.yml 中指定到我们的数据源配置启动

1
2
3
spring:
profiles:
active: druid

此时来测试一下启动,如果控制台出现以下信息就说明我们已经集成成功

image.png

测试

集成成功之后我们来使用并测试一下是否有效

假设现在我们有这么一个场景:在数据库 user 中存放着我们的用户服务相关的表,而在数据库 order 中存放着我们订单服务相关的表,我们想要有个接口来 实现查询每个用户下的所有订单。

创建表

数据库 user 中的表

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
/*
Navicat Premium Data Transfer

Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80032 (8.0.32)
Source Host : localhost:3306
Source Schema : dynamic1

Target Server Type : MySQL
Target Server Version : 80032 (8.0.32)
File Encoding : 65001

Date: 22/07/2023 18:06:44
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
`nickname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
`create_user_id` int NULL DEFAULT NULL COMMENT '创建用户id',
`create_user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建用户名称',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_user_id` int NULL DEFAULT NULL COMMENT '最后修改用户id',
`update_user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '最后修改用户名称',
`update_time` datetime NULL DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'zhagnsan', '123456', '张三', 0, '超级管理员', '2023-07-22 17:20:20', NULL, NULL, NULL);
INSERT INTO `sys_user` VALUES (2, 'lisi', '123456', '李四', 0, '超级管理员', '2023-07-22 17:20:32', NULL, NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

数据库 order 中的表

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
/*
Navicat Premium Data Transfer

Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80032 (8.0.32)
Source Host : localhost:3306
Source Schema : dynamic2

Target Server Type : MySQL
Target Server Version : 80032 (8.0.32)
File Encoding : 65001

Date: 22/07/2023 18:06:49
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`order_id` int NOT NULL AUTO_INCREMENT COMMENT '订单id',
`order_num` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '订单号',
`order_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '订单名称',
`create_user_id` int NULL DEFAULT NULL COMMENT '创建用户id',
`create_user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建用户名称',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_user_id` int NULL DEFAULT NULL COMMENT '最后修改用户id',
`update_user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '最后修改用户名称',
`update_time` datetime NULL DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_order
-- ----------------------------
INSERT INTO `t_order` VALUES (1, 'UzRs_20230722145842008', '手机', 1, '张三', '2023-07-22 17:21:49', NULL, NULL, NULL);
INSERT INTO `t_order` VALUES (2, 'AZps_20230722145842114', '电脑', 1, '张三', '2023-07-22 17:21:49', NULL, NULL, NULL);
INSERT INTO `t_order` VALUES (3, 'ZawS_20230722145843576', '平板', 1, '张三', '2023-07-22 17:21:49', NULL, NULL, NULL);
INSERT INTO `t_order` VALUES (4, 'YxIo_20230722145857663', '薯片', 2, '李四', '2023-07-22 17:21:49', NULL, NULL, NULL);
INSERT INTO `t_order` VALUES (5, 'ZcxY_20230722153244094', '手机', 2, '李四', '2023-07-22 17:21:49', NULL, NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

代码实现

在创建完表之后,我们可以使用 Mybaits Plus 的代码生成器将这两个表的代码生成出来,生成后的结构如下:

image.png

然后我们在 t_order 表的 MapperService 上加上一个注解:@DS(“order”) ,在 sys_user 表的 MapperService 上加上一个注解:@DS(“user”)

dynamic 中,我们就是通过@DS("数据源名称") 注解来实现数据源的切换的。

加完注解后如下:

image.png

image.png

image.png

image.png

然后我们就可以开始写我们的业务代码了,首先我们先创建一个 VO 来保存我们的信息

VO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.dynamic.module.order.entity.Order;
import com.dynamic.module.user.entity.User;
import lombok.Data;

import java.util.List;

/**
* @author Bummon
* @description
* @date 2023-07-22 17:41
*/
@Data
public class UserOrderVO extends User {

/**
* 订单列表
*/
private List<Order> orderList;

}

Controller

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
import com.dynamic.module.user.service.UserService;
import com.dynamic.module.user.vo.UserOrderVO;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* @author Bummon
* @description 用户管理
* @date 2023-07-22 17:40
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {

private final UserService userService;

@GetMapping("/order")
public List<UserOrderVO> getUserOrderList() {
return userService.getUserOrderList();
}

}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.dynamic.module.user.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dynamic.module.user.vo.UserOrderVO;

import java.util.List;

/**
* @author Bummon
* @description 针对表【sys_user】的数据库操作Service
* @createDate 2023-07-22 17:06:15
*/
public interface UserService extends IService<User> {

/**
* @return {@link List< UserOrderVO>}
* @date 2023-07-22 17:42
* @author Bummon
* @description 查询用户订单列表
*/
List<UserOrderVO> getUserOrderList();

}

ServiceImpl

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
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dynamic.module.order.entity.Order;
import com.dynamic.module.order.mapper.OrderMapper;
import com.dynamic.module.user.entity.User;
import com.dynamic.module.user.mapper.UserMapper;
import com.dynamic.module.user.service.UserService;
import com.dynamic.module.user.vo.UserOrderVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @author Bummon
* @description 针对表【sys_user】的数据库操作Service实现
* @createDate 2023-07-22 17:06:15
*/
@DS("user")
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {

private final OrderMapper orderMapper;

@Override
public List<UserOrderVO> getUserOrderList() {
List<User> userList = this.list();
List<UserOrderVO> userOrderList = BeanUtil.copyToList(userList, UserOrderVO.class);
userOrderList.forEach(user -> {
List<Order> orderList = orderMapper.selectList(Wrappers.lambdaQuery(Order.class)
.eq(Order::getCreateUserId, user.getUserId()));
user.setOrderList(orderList);
});
return userOrderList;
}
}

测试

代码编写完成之后,我们使用 Postman 来测试一下得到的是否是我们预期的结果

image.png

最终得到的结果如下:

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
[
{
"userId": 1,
"username": "zhagnsan",
"password": "123456",
"nickname": "张三",
"createUserId": 0,
"createUserName": "超级管理员",
"createTime": "2023-07-22T09:20:20.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null,
"orderList": [
{
"orderId": 1,
"orderNum": "UzRs_20230722145842008",
"orderName": "手机",
"createUserId": 1,
"createUserName": "张三",
"createTime": "2023-07-22T09:21:49.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null
},
{
"orderId": 2,
"orderNum": "AZps_20230722145842114",
"orderName": "电脑",
"createUserId": 1,
"createUserName": "张三",
"createTime": "2023-07-22T09:21:49.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null
},
{
"orderId": 3,
"orderNum": "ZawS_20230722145843576",
"orderName": "平板",
"createUserId": 1,
"createUserName": "张三",
"createTime": "2023-07-22T09:21:49.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null
}
]
},
{
"userId": 2,
"username": "lisi",
"password": "123456",
"nickname": "李四",
"createUserId": 0,
"createUserName": "超级管理员",
"createTime": "2023-07-22T09:20:32.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null,
"orderList": [
{
"orderId": 4,
"orderNum": "YxIo_20230722145857663",
"orderName": "薯片",
"createUserId": 2,
"createUserName": "李四",
"createTime": "2023-07-22T09:21:49.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null
},
{
"orderId": 5,
"orderNum": "ZcxY_20230722153244094",
"orderName": "手机",
"createUserId": 2,
"createUserName": "李四",
"createTime": "2023-07-22T09:21:49.000+00:00",
"updateUserId": null,
"updateUserName": null,
"updateTime": null
}
]
}
]

可以看到结果确实为我们预期的结果。

至此,教程就结束啦,感谢大家的关注