| @@ -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 | |||