| @@ -0,0 +1,382 @@ | |||||
| # spring-boot-demo-multi-datasource-mybatis | |||||
| > 此 demo 主要演示了 Spring Boot 如何集成 Mybatis 的多数据源。可以自己基于AOP实现多数据源,这里基于 Mybatis-Plus 提供的一个优雅的开源的解决方案来实现。 | |||||
| ## 准备工作 | |||||
| 准备两个数据源,分别执行如下建表语句 | |||||
| ```mysql | |||||
| DROP TABLE IF EXISTS `multi_user`; | |||||
| CREATE TABLE `multi_user`( | |||||
| `id` bigint(64) NOT NULL, | |||||
| `name` varchar(50) DEFAULT NULL, | |||||
| `age` int(30) DEFAULT NULL, | |||||
| PRIMARY KEY (`id`) USING BTREE | |||||
| ) ENGINE = InnoDB | |||||
| AUTO_INCREMENT = 1 | |||||
| CHARACTER SET = utf8 | |||||
| COLLATE = utf8_general_ci; | |||||
| ``` | |||||
| ## 导入依赖 | |||||
| ```xml | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>spring-boot-demo-multi-datasource-mybatis</artifactId> | |||||
| <version>1.0.0-SNAPSHOT</version> | |||||
| <packaging>jar</packaging> | |||||
| <name>spring-boot-demo-multi-datasource-mybatis</name> | |||||
| <description>Demo project for Spring Boot</description> | |||||
| <parent> | |||||
| <groupId>com.xkcoding</groupId> | |||||
| <artifactId>spring-boot-demo</artifactId> | |||||
| <version>1.0.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||||
| <java.version>1.8</java.version> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-starter-test</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>mysql</groupId> | |||||
| <artifactId>mysql-connector-java</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.baomidou</groupId> | |||||
| <artifactId>dynamic-datasource-spring-boot-starter</artifactId> | |||||
| <version>2.5.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.baomidou</groupId> | |||||
| <artifactId>mybatis-plus-boot-starter</artifactId> | |||||
| <version>3.0.7.1</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.projectlombok</groupId> | |||||
| <artifactId>lombok</artifactId> | |||||
| <optional>true</optional> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>cn.hutool</groupId> | |||||
| <artifactId>hutool-all</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.guava</groupId> | |||||
| <artifactId>guava</artifactId> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <finalName>spring-boot-demo-multi-datasource-mybatis</finalName> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| ``` | |||||
| ## 准备实体类 | |||||
| `User.java` | |||||
| > 1. @Data / @NoArgsConstructor / @AllArgsConstructor / @Builder 都是 lombok 注解 | |||||
| > 2. @TableName("multi_user") 是 Mybatis-Plus 注解,主要是当实体类名字和表名不满足 **驼峰和下划线互转** 的格式时,用于表示数据库表名 | |||||
| > 3. @TableId(type = IdType.ID_WORKER) 是 Mybatis-Plus 注解,主要是指定主键类型,这里我使用的是 Mybatis-Plus 基于 twitter 提供的 雪花算法 | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * User实体类 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis.model | |||||
| * @description: User实体类 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:19 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| @Data | |||||
| @TableName("multi_user") | |||||
| @NoArgsConstructor | |||||
| @AllArgsConstructor | |||||
| @Builder | |||||
| public class User implements Serializable { | |||||
| private static final long serialVersionUID = -1923859222295750467L; | |||||
| /** | |||||
| * 主键 | |||||
| */ | |||||
| @TableId(type = IdType.ID_WORKER) | |||||
| private Long id; | |||||
| /** | |||||
| * 姓名 | |||||
| */ | |||||
| private String name; | |||||
| /** | |||||
| * 年龄 | |||||
| */ | |||||
| private Integer age; | |||||
| } | |||||
| ``` | |||||
| ## 数据访问层 | |||||
| `UserMapper.java` | |||||
| > 不需要建对应的xml,只需要继承 BaseMapper 就拥有了大部分单表操作的方法了。 | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * 数据访问层 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis.mapper | |||||
| * @description: 数据访问层 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:28 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| public interface UserMapper extends BaseMapper<User> { | |||||
| } | |||||
| ``` | |||||
| ## 数据服务层 | |||||
| ### 接口 | |||||
| `UserService.java` | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * 数据服务层 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis.service | |||||
| * @description: 数据服务层 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:31 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| public interface UserService extends IService<User> { | |||||
| /** | |||||
| * 添加 User | |||||
| * | |||||
| * @param user 用户 | |||||
| */ | |||||
| void addUser(User user); | |||||
| } | |||||
| ``` | |||||
| ### 实现 | |||||
| `UserServiceImpl.java` | |||||
| > 1. @DS: 注解在类上或方法上来切换数据源,方法上的@DS优先级大于类上的@DS | |||||
| > 2. baseMapper: mapper 对象,即`UserMapper`,可获得CRUD功能 | |||||
| > 3. 默认走从库: `@DS(value = "slave")`在类上,默认走从库,除非在方法在添加`@DS(value = "master")`才走主库 | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * 数据服务层 实现 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis.service.impl | |||||
| * @description: 数据服务层 实现 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:37 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| @Service | |||||
| @DS("slave") | |||||
| public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { | |||||
| /** | |||||
| * 类上 {@code @DS("slave")} 代表默认从库,在方法上写 {@code @DS("master")} 代表默认主库 | |||||
| * | |||||
| * @param user 用户 | |||||
| */ | |||||
| @DS("master") | |||||
| @Override | |||||
| public void addUser(User user) { | |||||
| baseMapper.insert(user); | |||||
| } | |||||
| } | |||||
| ``` | |||||
| ## 启动类 | |||||
| `SpringBootDemoMultiDatasourceMybatisApplication.java` | |||||
| > 启动类上方需要使用@MapperScan扫描 mapper 类所在的包 | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * 启动器 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis | |||||
| * @description: 启动器 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:19 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| @SpringBootApplication | |||||
| @MapperScan(basePackages = "com.xkcoding.multi.datasource.mybatis.mapper") | |||||
| public class SpringBootDemoMultiDatasourceMybatisApplication { | |||||
| public static void main(String[] args) { | |||||
| SpringApplication.run(SpringBootDemoMultiDatasourceMybatisApplication.class, args); | |||||
| } | |||||
| } | |||||
| ``` | |||||
| ## 配置文件 | |||||
| `application.yml` | |||||
| ```yaml | |||||
| spring: | |||||
| datasource: | |||||
| dynamic: | |||||
| datasource: | |||||
| master: | |||||
| username: root | |||||
| password: root | |||||
| url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8 | |||||
| driver-class-name: com.mysql.cj.jdbc.Driver | |||||
| slave: | |||||
| username: root | |||||
| password: root | |||||
| url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo-2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8 | |||||
| driver-class-name: com.mysql.cj.jdbc.Driver | |||||
| mp-enabled: true | |||||
| logging: | |||||
| level: | |||||
| com.xkcoding.multi.datasource.mybatis: debug | |||||
| ``` | |||||
| ## 测试类 | |||||
| ```java | |||||
| /** | |||||
| * <p> | |||||
| * 测试主从数据源 | |||||
| * </p> | |||||
| * | |||||
| * @package: com.xkcoding.multi.datasource.mybatis.service.impl | |||||
| * @description: 测试主从数据源 | |||||
| * @author: yangkai.shen | |||||
| * @date: Created in 2019-01-21 14:45 | |||||
| * @copyright: Copyright (c) 2019 | |||||
| * @version: V1.0 | |||||
| * @modified: yangkai.shen | |||||
| */ | |||||
| @Slf4j | |||||
| public class UserServiceImplTest extends SpringBootDemoMultiDatasourceMybatisApplicationTests { | |||||
| @Autowired | |||||
| private UserService userService; | |||||
| /** | |||||
| * 主从库添加 | |||||
| */ | |||||
| @Test | |||||
| public void addUser() { | |||||
| User userMaster = User.builder().name("主库添加").age(20).build(); | |||||
| userService.addUser(userMaster); | |||||
| User userSlave = User.builder().name("从库添加").age(20).build(); | |||||
| userService.save(userSlave); | |||||
| } | |||||
| /** | |||||
| * 从库查询 | |||||
| */ | |||||
| @Test | |||||
| public void testListUser() { | |||||
| List<User> list = userService.list(new QueryWrapper<>()); | |||||
| log.info("【list】= {}", JSONUtil.toJsonStr(list)); | |||||
| } | |||||
| } | |||||
| ``` | |||||
| ### 测试结果 | |||||
| 主从数据源加载成功 | |||||
| ```java | |||||
| 2019-01-21 14:55:41.096 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : master - Starting... | |||||
| 2019-01-21 14:55:41.307 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : master - Start completed. | |||||
| 2019-01-21 14:55:41.308 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : slave - Starting... | |||||
| 2019-01-21 14:55:41.312 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : slave - Start completed. | |||||
| 2019-01-21 14:55:41.312 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 初始共加载 2 个数据源 | |||||
| 2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 动态数据源-加载 slave 成功 | |||||
| 2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 动态数据源-加载 master 成功 | |||||
| 2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 当前的默认数据源是单数据源,数据源名为 master | |||||
| _ _ |_ _ _|_. ___ _ | _ | |||||
| | | |\/|_)(_| | |_\ |_)||_|_\ | |||||
| / | | |||||
| 3.0.7.1 | |||||
| ``` | |||||
| **主**库 **建议** 只执行 **INSERT** **UPDATE** **DELETE** 操作 | |||||
|  | |||||
| **从**库 **建议** 只执行 **SELECT** 操作 | |||||
|  | |||||
| > 生产环境需要搭建 **主从复制** | |||||
| ## 参考 | |||||
| 1. Mybatis-Plus 多数据源文档:https://mybatis.plus/guide/dynamic-datasource.html | |||||
| 2. Mybatis-Plus 多数据源集成官方 demo:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/tree/master/samples | |||||