|
| 1 | +# spring-boot-demo-multi-datasource-mybatis |
| 2 | + |
| 3 | +> 此 demo 主要演示了 Spring Boot 如何集成 Mybatis 的多数据源。可以自己基于AOP实现多数据源,这里基于 Mybatis-Plus 提供的一个优雅的开源的解决方案来实现。 |
| 4 | +
|
| 5 | +## 准备工作 |
| 6 | + |
| 7 | +准备两个数据源,分别执行如下建表语句 |
| 8 | + |
| 9 | +```mysql |
| 10 | +DROP TABLE IF EXISTS `multi_user`; |
| 11 | +CREATE TABLE `multi_user`( |
| 12 | + `id` bigint(64) NOT NULL, |
| 13 | + `name` varchar(50) DEFAULT NULL, |
| 14 | + `age` int(30) DEFAULT NULL, |
| 15 | + PRIMARY KEY (`id`) USING BTREE |
| 16 | +) ENGINE = InnoDB |
| 17 | + AUTO_INCREMENT = 1 |
| 18 | + CHARACTER SET = utf8 |
| 19 | + COLLATE = utf8_general_ci; |
| 20 | +``` |
| 21 | + |
| 22 | +## 导入依赖 |
| 23 | + |
| 24 | +```xml |
| 25 | +<?xml version="1.0" encoding="UTF-8"?> |
| 26 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 27 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| 28 | + <modelVersion>4.0.0</modelVersion> |
| 29 | + |
| 30 | + <artifactId>spring-boot-demo-multi-datasource-mybatis</artifactId> |
| 31 | + <version>1.0.0-SNAPSHOT</version> |
| 32 | + <packaging>jar</packaging> |
| 33 | + |
| 34 | + <name>spring-boot-demo-multi-datasource-mybatis</name> |
| 35 | + <description>Demo project for Spring Boot</description> |
| 36 | + |
| 37 | + <parent> |
| 38 | + <groupId>com.xkcoding</groupId> |
| 39 | + <artifactId>spring-boot-demo</artifactId> |
| 40 | + <version>1.0.0-SNAPSHOT</version> |
| 41 | + </parent> |
| 42 | + |
| 43 | + <properties> |
| 44 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 45 | + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
| 46 | + <java.version>1.8</java.version> |
| 47 | + </properties> |
| 48 | + |
| 49 | + <dependencies> |
| 50 | + <dependency> |
| 51 | + <groupId>org.springframework.boot</groupId> |
| 52 | + <artifactId>spring-boot-starter</artifactId> |
| 53 | + </dependency> |
| 54 | + |
| 55 | + <dependency> |
| 56 | + <groupId>org.springframework.boot</groupId> |
| 57 | + <artifactId>spring-boot-starter-test</artifactId> |
| 58 | + <scope>test</scope> |
| 59 | + </dependency> |
| 60 | + |
| 61 | + <dependency> |
| 62 | + <groupId>mysql</groupId> |
| 63 | + <artifactId>mysql-connector-java</artifactId> |
| 64 | + </dependency> |
| 65 | + |
| 66 | + <dependency> |
| 67 | + <groupId>com.baomidou</groupId> |
| 68 | + <artifactId>dynamic-datasource-spring-boot-starter</artifactId> |
| 69 | + <version>2.5.0</version> |
| 70 | + </dependency> |
| 71 | + |
| 72 | + <dependency> |
| 73 | + <groupId>com.baomidou</groupId> |
| 74 | + <artifactId>mybatis-plus-boot-starter</artifactId> |
| 75 | + <version>3.0.7.1</version> |
| 76 | + </dependency> |
| 77 | + |
| 78 | + <dependency> |
| 79 | + <groupId>org.projectlombok</groupId> |
| 80 | + <artifactId>lombok</artifactId> |
| 81 | + <optional>true</optional> |
| 82 | + </dependency> |
| 83 | + |
| 84 | + <dependency> |
| 85 | + <groupId>cn.hutool</groupId> |
| 86 | + <artifactId>hutool-all</artifactId> |
| 87 | + </dependency> |
| 88 | + |
| 89 | + <dependency> |
| 90 | + <groupId>com.google.guava</groupId> |
| 91 | + <artifactId>guava</artifactId> |
| 92 | + </dependency> |
| 93 | + </dependencies> |
| 94 | + |
| 95 | + <build> |
| 96 | + <finalName>spring-boot-demo-multi-datasource-mybatis</finalName> |
| 97 | + <plugins> |
| 98 | + <plugin> |
| 99 | + <groupId>org.springframework.boot</groupId> |
| 100 | + <artifactId>spring-boot-maven-plugin</artifactId> |
| 101 | + </plugin> |
| 102 | + </plugins> |
| 103 | + </build> |
| 104 | + |
| 105 | +</project> |
| 106 | +``` |
| 107 | + |
| 108 | +## 准备实体类 |
| 109 | + |
| 110 | +`User.java` |
| 111 | + |
| 112 | +> 1. @Data / @NoArgsConstructor / @AllArgsConstructor / @Builder 都是 lombok 注解 |
| 113 | +> 2. @TableName("multi_user") 是 Mybatis-Plus 注解,主要是当实体类名字和表名不满足 **驼峰和下划线互转** 的格式时,用于表示数据库表名 |
| 114 | +> 3. @TableId(type = IdType.ID_WORKER) 是 Mybatis-Plus 注解,主要是指定主键类型,这里我使用的是 Mybatis-Plus 基于 twitter 提供的 雪花算法 |
| 115 | +
|
| 116 | +```java |
| 117 | +/** |
| 118 | + * <p> |
| 119 | + * User实体类 |
| 120 | + * </p> |
| 121 | + * |
| 122 | + * @package: com.xkcoding.multi.datasource.mybatis.model |
| 123 | + * @description: User实体类 |
| 124 | + * @author: yangkai.shen |
| 125 | + * @date: Created in 2019-01-21 14:19 |
| 126 | + * @copyright: Copyright (c) 2019 |
| 127 | + * @version: V1.0 |
| 128 | + * @modified: yangkai.shen |
| 129 | + */ |
| 130 | +@Data |
| 131 | +@TableName("multi_user") |
| 132 | +@NoArgsConstructor |
| 133 | +@AllArgsConstructor |
| 134 | +@Builder |
| 135 | +public class User implements Serializable { |
| 136 | + private static final long serialVersionUID = -1923859222295750467L; |
| 137 | + |
| 138 | + /** |
| 139 | + * 主键 |
| 140 | + */ |
| 141 | + @TableId(type = IdType.ID_WORKER) |
| 142 | + private Long id; |
| 143 | + |
| 144 | + /** |
| 145 | + * 姓名 |
| 146 | + */ |
| 147 | + private String name; |
| 148 | + |
| 149 | + /** |
| 150 | + * 年龄 |
| 151 | + */ |
| 152 | + private Integer age; |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +## 数据访问层 |
| 157 | + |
| 158 | +`UserMapper.java` |
| 159 | + |
| 160 | +> 不需要建对应的xml,只需要继承 BaseMapper 就拥有了大部分单表操作的方法了。 |
| 161 | +
|
| 162 | +```java |
| 163 | +/** |
| 164 | + * <p> |
| 165 | + * 数据访问层 |
| 166 | + * </p> |
| 167 | + * |
| 168 | + * @package: com.xkcoding.multi.datasource.mybatis.mapper |
| 169 | + * @description: 数据访问层 |
| 170 | + * @author: yangkai.shen |
| 171 | + * @date: Created in 2019-01-21 14:28 |
| 172 | + * @copyright: Copyright (c) 2019 |
| 173 | + * @version: V1.0 |
| 174 | + * @modified: yangkai.shen |
| 175 | + */ |
| 176 | +public interface UserMapper extends BaseMapper<User> { |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +## 数据服务层 |
| 181 | + |
| 182 | +### 接口 |
| 183 | + |
| 184 | +`UserService.java` |
| 185 | + |
| 186 | +```java |
| 187 | +/** |
| 188 | + * <p> |
| 189 | + * 数据服务层 |
| 190 | + * </p> |
| 191 | + * |
| 192 | + * @package: com.xkcoding.multi.datasource.mybatis.service |
| 193 | + * @description: 数据服务层 |
| 194 | + * @author: yangkai.shen |
| 195 | + * @date: Created in 2019-01-21 14:31 |
| 196 | + * @copyright: Copyright (c) 2019 |
| 197 | + * @version: V1.0 |
| 198 | + * @modified: yangkai.shen |
| 199 | + */ |
| 200 | +public interface UserService extends IService<User> { |
| 201 | + |
| 202 | + /** |
| 203 | + * 添加 User |
| 204 | + * |
| 205 | + * @param user 用户 |
| 206 | + */ |
| 207 | + void addUser(User user); |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +### 实现 |
| 212 | + |
| 213 | +`UserServiceImpl.java` |
| 214 | + |
| 215 | +> 1. @DS: 注解在类上或方法上来切换数据源,方法上的@DS优先级大于类上的@DS |
| 216 | +> 2. baseMapper: mapper 对象,即`UserMapper`,可获得CRUD功能 |
| 217 | +> 3. 默认走从库: `@DS(value = "slave")`在类上,默认走从库,除非在方法在添加`@DS(value = "master")`才走主库 |
| 218 | +
|
| 219 | +```java |
| 220 | +/** |
| 221 | + * <p> |
| 222 | + * 数据服务层 实现 |
| 223 | + * </p> |
| 224 | + * |
| 225 | + * @package: com.xkcoding.multi.datasource.mybatis.service.impl |
| 226 | + * @description: 数据服务层 实现 |
| 227 | + * @author: yangkai.shen |
| 228 | + * @date: Created in 2019-01-21 14:37 |
| 229 | + * @copyright: Copyright (c) 2019 |
| 230 | + * @version: V1.0 |
| 231 | + * @modified: yangkai.shen |
| 232 | + */ |
| 233 | +@Service |
| 234 | +@DS("slave") |
| 235 | +public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { |
| 236 | + |
| 237 | + /** |
| 238 | + * 类上 {@code @DS("slave")} 代表默认从库,在方法上写 {@code @DS("master")} 代表默认主库 |
| 239 | + * |
| 240 | + * @param user 用户 |
| 241 | + */ |
| 242 | + @DS("master") |
| 243 | + @Override |
| 244 | + public void addUser(User user) { |
| 245 | + baseMapper.insert(user); |
| 246 | + } |
| 247 | +} |
| 248 | +``` |
| 249 | + |
| 250 | +## 启动类 |
| 251 | + |
| 252 | +`SpringBootDemoMultiDatasourceMybatisApplication.java` |
| 253 | + |
| 254 | +> 启动类上方需要使用@MapperScan扫描 mapper 类所在的包 |
| 255 | +
|
| 256 | +```java |
| 257 | +/** |
| 258 | + * <p> |
| 259 | + * 启动器 |
| 260 | + * </p> |
| 261 | + * |
| 262 | + * @package: com.xkcoding.multi.datasource.mybatis |
| 263 | + * @description: 启动器 |
| 264 | + * @author: yangkai.shen |
| 265 | + * @date: Created in 2019-01-21 14:19 |
| 266 | + * @copyright: Copyright (c) 2019 |
| 267 | + * @version: V1.0 |
| 268 | + * @modified: yangkai.shen |
| 269 | + */ |
| 270 | +@SpringBootApplication |
| 271 | +@MapperScan(basePackages = "com.xkcoding.multi.datasource.mybatis.mapper") |
| 272 | +public class SpringBootDemoMultiDatasourceMybatisApplication { |
| 273 | + |
| 274 | + public static void main(String[] args) { |
| 275 | + SpringApplication.run(SpringBootDemoMultiDatasourceMybatisApplication.class, args); |
| 276 | + } |
| 277 | + |
| 278 | +} |
| 279 | +``` |
| 280 | + |
| 281 | +## 配置文件 |
| 282 | + |
| 283 | +`application.yml` |
| 284 | + |
| 285 | +```yaml |
| 286 | +spring: |
| 287 | + datasource: |
| 288 | + dynamic: |
| 289 | + datasource: |
| 290 | + master: |
| 291 | + username: root |
| 292 | + password: root |
| 293 | + 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 |
| 294 | + driver-class-name: com.mysql.cj.jdbc.Driver |
| 295 | + slave: |
| 296 | + username: root |
| 297 | + password: root |
| 298 | + 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 |
| 299 | + driver-class-name: com.mysql.cj.jdbc.Driver |
| 300 | + mp-enabled: true |
| 301 | +logging: |
| 302 | + level: |
| 303 | + com.xkcoding.multi.datasource.mybatis: debug |
| 304 | +``` |
| 305 | +
|
| 306 | +## 测试类 |
| 307 | +
|
| 308 | +```java |
| 309 | +/** |
| 310 | + * <p> |
| 311 | + * 测试主从数据源 |
| 312 | + * </p> |
| 313 | + * |
| 314 | + * @package: com.xkcoding.multi.datasource.mybatis.service.impl |
| 315 | + * @description: 测试主从数据源 |
| 316 | + * @author: yangkai.shen |
| 317 | + * @date: Created in 2019-01-21 14:45 |
| 318 | + * @copyright: Copyright (c) 2019 |
| 319 | + * @version: V1.0 |
| 320 | + * @modified: yangkai.shen |
| 321 | + */ |
| 322 | +@Slf4j |
| 323 | +public class UserServiceImplTest extends SpringBootDemoMultiDatasourceMybatisApplicationTests { |
| 324 | + @Autowired |
| 325 | + private UserService userService; |
| 326 | + |
| 327 | + /** |
| 328 | + * 主从库添加 |
| 329 | + */ |
| 330 | + @Test |
| 331 | + public void addUser() { |
| 332 | + User userMaster = User.builder().name("主库添加").age(20).build(); |
| 333 | + userService.addUser(userMaster); |
| 334 | + |
| 335 | + User userSlave = User.builder().name("从库添加").age(20).build(); |
| 336 | + userService.save(userSlave); |
| 337 | + } |
| 338 | + |
| 339 | + /** |
| 340 | + * 从库查询 |
| 341 | + */ |
| 342 | + @Test |
| 343 | + public void testListUser() { |
| 344 | + List<User> list = userService.list(new QueryWrapper<>()); |
| 345 | + log.info("【list】= {}", JSONUtil.toJsonStr(list)); |
| 346 | + } |
| 347 | +} |
| 348 | +``` |
| 349 | + |
| 350 | +### 测试结果 |
| 351 | + |
| 352 | +主从数据源加载成功 |
| 353 | + |
| 354 | +```java |
| 355 | +2019-01-21 14:55:41.096 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : master - Starting... |
| 356 | +2019-01-21 14:55:41.307 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : master - Start completed. |
| 357 | +2019-01-21 14:55:41.308 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : slave - Starting... |
| 358 | +2019-01-21 14:55:41.312 INFO 7239 --- [ main] com.zaxxer.hikari.HikariDataSource : slave - Start completed. |
| 359 | +2019-01-21 14:55:41.312 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 初始共加载 2 个数据源 |
| 360 | +2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 动态数据源-加载 slave 成功 |
| 361 | +2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 动态数据源-加载 master 成功 |
| 362 | +2019-01-21 14:55:41.313 INFO 7239 --- [ main] c.b.d.d.DynamicRoutingDataSource : 当前的默认数据源是单数据源,数据源名为 master |
| 363 | + _ _ |_ _ _|_. ___ _ | _ |
| 364 | +| | |\/|_)(_| | |_\ |_)||_|_\ |
| 365 | + / | |
| 366 | + 3.0.7.1 |
| 367 | +``` |
| 368 | + |
| 369 | +**主**库 **建议** 只执行 **INSERT** **UPDATE** **DELETE** 操作 |
| 370 | + |
| 371 | + |
| 372 | + |
| 373 | +**从**库 **建议** 只执行 **SELECT** 操作 |
| 374 | + |
| 375 | + |
| 376 | + |
| 377 | +> 生产环境需要搭建 **主从复制** |
| 378 | +
|
| 379 | +## 参考 |
| 380 | + |
| 381 | +1. Mybatis-Plus 多数据源文档:https://mybatis.plus/guide/dynamic-datasource.html |
| 382 | +2. Mybatis-Plus 多数据源集成官方 demo:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/tree/master/samples |
0 commit comments