Mybatis Multiple Data Sources Read-Write Separation (Annotation Implementation)
First, we need to build two libraries for testing. I use master_test and slave_test libraries here. Both libraries have the same table (lazy, happy), table structure.
Table name t_user
| Field Name | Type | Note|
| :------: | :------: | :------: |
| id | int | Primary key self-increasing ID|
| name | varchar | name|
Two different data are added to the table to facilitate testing.
The main database record name is xiaobin, and the slave database record name is xiaoliu.
Start using Springboot to integrate mybatis, first introduce the pom file
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <groupId>com.xiaobin</groupId> <artifactId>mysql_master_slave</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <lombok.version>1.18.6</lombok.version> <mybatis.version>1.3.2</mybatis.version> <lombox.version>1.18.6</lombox.version> </properties> <dependencies> <!-- Add to web Starting coordinates --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Add to lombok Tool coordinates --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <!-- Add to springboot Test coordinates --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- Add to lombox Test coordinates --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombox.version}</version> </dependency> <!-- Add to mybatis Dependent coordinates --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <!-- Add to mysql Driver coordinates --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- Add to druid Data source coordinates --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- Add to AOP coordinate --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> </project>
Dynamic Data Source Configuration
The data source used here is druid, which realizes switching between data sources by using @DataSource custom annotation and configuring Aop for switching.
application.yml configuration file
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: xiaobin-master: # Master data source driverClassName: com.mysql.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/master_test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8 xiaobin-slave: # From data sources driverClassName: com.mysql.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/slave_test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8 mybatis: mapper-locations: classpath:mapper/*.xml
Multiple Data Source Configuration Class
package com.xiaobin.config; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * Creation time: 2019/9/22 11:42 * Note: Multiple Data Source Configuration Information * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ @Configuration @Component public class DynamicDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.druid.xiaobin-master") public DataSource xiaobinMasterDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.xiaobin-slave") public DataSource xiaobinSlaveDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dataSource(DataSource xiaobinMasterDataSource, DataSource xiaobinSlaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("xiaobin-master",xiaobinMasterDataSource); targetDataSources.put("xiaobin-slave", xiaobinSlaveDataSource); return new DynamicDataSource(xiaobinMasterDataSource, targetDataSources); } }
Dynamic Data Source Switching Class
package com.xiaobin.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.lang.Nullable; import javax.sql.DataSource; import java.util.Map; /** * Creation time: 2019/9/22 11:51 * Note: Dynamic Data Sources * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
Custom @DataSource annotation
Add this annotation to Dao that needs to switch data
package com.xiaobin.annotation; import java.lang.annotation.*; /** * Creation time: 2019/9/22 11:53 * Note: Custom Data Source Selection Annotation * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String name() default ""; }
Aop Section Class Configuration
package com.xiaobin.aspect; import com.xiaobin.annotation.DataSource; import com.xiaobin.config.DynamicDataSource; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * Creation time: 2019/9/22 11:54 * Remarks: * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.xiaobin.annotation.DataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if(dataSource == null){ DynamicDataSource.setDataSource("xiaobin-master"); }else { DynamicDataSource.setDataSource(dataSource.name()); } try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); } } }
Start configuration annotation information, important (otherwise running will report errors)
package com.xiaobin; import com.xiaobin.config.DynamicDataSourceConfig; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.Import; /** * Creation time: 2019/9/22 11:17 * Remarks: * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) @MapperScan(basePackages = "com.xiaobin.mapper") @Import({DynamicDataSourceConfig.class}) public class StartApp { public static void main(String[] args) { SpringApplication.run(StartApp.class); } }
Test controller
package com.xiaobin.api; import com.xiaobin.Entity.TUser; import com.xiaobin.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * Creation time: 2019/9/22 12:08 * Remarks: * Binong Self-taught Exchange Group: 260532022. Welcome to join us. It's a pleasure to share learning. **/ @RestController @RequestMapping public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/{name}/list") public List<TUser> list(@PathVariable("name")String name){ if(name.equals("master")){ return userMapper.queryAllWithMaster(); }else{ return userMapper.queryAllWithSlave(); } } }
Design sketch
More path value transfer, master-slave data source switching
directory structure
Source address (database needs to be created by itself) https://gitee.com/MyXiaoXiaoB...