看了好几篇博友写的文章,关于spring-boot整合mybatis-plus实现读写分离,不过都是缺这少那的,跑不起来,所以自己实操了一次,做个记录
实现方式为使用Aop切面
1、增加数据库枚举类- /**
- * 数据库类型
- */
- public enum DBTypeEnum {
- /**
- * 主节点
- */
- MASTER,
- /**
- * 从
- */
- SLAVE
- }
复制代码 2、配置数据源- /**
- * 多数据源配置
- */
- @AutoConfigureBefore(DruidDataSourceAutoConfigure.class)
- @Configuration
- @ConfigurationProperties(prefix = "spring.datasource.druid")
- @Data
- public class DataSourceConfig {
- private int initialSize;
- private int maxActive;
- private int minIdle;
- private long maxWait;
- private long minEvictableIdleTimeMillis;
- private long timeBetweenEvictionRunsMillis;
- private boolean testWhileIdle;
- /**
- * 配置主数据源
- *
- * @return 数据源
- */
- @Bean(name = "masterDataSource")
- @ConfigurationProperties(prefix = "spring.datasource.druid.master" )
- public DataSource masterDataSource() {
- DruidDataSource druidDataSource = new DruidDataSource();
- parseDruidConfig(druidDataSource);
- return druidDataSource;
- }
- /**
- * 配置从数据源
- *
- * @return 数据源
- */
- @Bean(name = "slaveDataSource")
- @ConfigurationProperties(prefix = "spring.datasource.druid.slave")
- public DataSource slaveDataSource() {
- DruidDataSource druidDataSource = new DruidDataSource();
- parseDruidConfig(druidDataSource);
- return druidDataSource;
- }
- private void parseDruidConfig(DruidDataSource dataSource) {
- dataSource.setInitialSize(initialSize);
- dataSource.setMaxActive(maxActive);
- dataSource.setMinIdle(minIdle);
- dataSource.setMaxWait(maxWait);
- dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
- dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
- dataSource.setTestWhileIdle(testWhileIdle);
- }
- /**
- * 配置路由数据源
- *
- * @param masterDataSource 主节点
- * @param slaveDataSource 从节点
- * @return 数据源
- */
- @Bean(name = "myRoutingDataSource")
- @DependsOn({"masterDataSource", "slaveDataSource"})
- @Primary
- public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
- @Qualifier("slaveDataSource") DataSource slaveDataSource) {
- Map<Object, Object> targetDataSources = new HashMap<>(3);
- targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
- targetDataSources.put(DBTypeEnum.SLAVE, slaveDataSource);
- MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
- //设置默认数据源
- myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
- myRoutingDataSource.setTargetDataSources(targetDataSources);
- return myRoutingDataSource;
- }
- }
复制代码 3、线程轮循切换主从数据库(多主多从的情况下适用)- /**
- * 通过ThreadLocal将数据源设置到每个线程上下文中
- */
- public class DataSourceContextHolder {
- private static final ThreadLocal<DBTypeEnum> CONTEXT_HOLDER = new ThreadLocal<>();
- private static final AtomicInteger COUNTER = new AtomicInteger(-1);
- public static void set(DBTypeEnum dbType) {
- CONTEXT_HOLDER.set(dbType);
- }
- public static DBTypeEnum get() {
- return CONTEXT_HOLDER.get();
- }
- public static void clear(){
- CONTEXT_HOLDER.remove();
- }
- public static void master() {
- set(DBTypeEnum.MASTER);
- System.out.println("切换到master");
- }
- public static void slave() {
- // 轮询
- int index = COUNTER.getAndIncrement() % 2;
- if (COUNTER.get() > 9999) {
- COUNTER.set(-1);
- }
- // if (index == 0) {
- // set(DBTypeEnum.SLAVE1);
- // System.out.println("切换到slave1");
- // } else {
- // set(DBTypeEnum.SLAVE2);
- // System.out.println("切换到slave2");
- // }
- set(DBTypeEnum.SLAVE);
- System.out.println("切换到slave2");
- }
- }
复制代码 4、声明路由数据源key(多主多从的情况下适用)- /**
- * 声明路由数据源key
- */
- public class MyRoutingDataSource extends AbstractRoutingDataSource {
- @Nullable
- @Override
- protected Object determineCurrentLookupKey() {
- return DataSourceContextHolder.get();
- }
- }
复制代码 5、强制使用主/从数据库注解- /**
- * 强制使用主数据库注解
- */
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD})
- public @interface DataSourceMaster {
- }
复制代码- /**
- * 强制使用从数据库注解
- */
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD})
- public @interface DataSourceSlave {
- }
复制代码 6、切面类(从库注解会报错,不知道什么原因)- /**
- * 使用aop实现数据源切换
- */
- @Aspect
- @Component
- public class DataSourceAop {
- // /**
- // * 需要读的方法,切面
- // */
- // @Pointcut("!@annotation(com.readWriteSeparation.annotation.DataSourceSlave)" +
- // "|| execution(* com.readWriteSeparation.service..*.select*(..)) " +
- // "|| execution(* com.readWriteSeparation.service..*.get*(..))" +
- // "|| execution(* com.readWriteSeparation.service..*.query*(..))" +
- // "|| execution(* com.readWriteSeparation.service..*.find*(..)))")
- // public void readPointcut() { }
- /**
- * 需要读的方法,切面
- */
- @Pointcut("execution(* com.readWriteSeparation.service..*.select*(..)) " +
- "|| execution(* com.readWriteSeparation.service..*.get*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.query*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.find*(..)))")
- public void readPointcut() { }
- /**
- * 写切面
- */
- @Pointcut("@annotation(com.readWriteSeparation.annotation.DataSourceMaster) " +
- "|| execution(* com.readWriteSeparation.service..*.insert*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.save*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.add*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.update*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.edit*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.delete*(..))" +
- "|| execution(* com.readWriteSeparation.service..*.remove*(..))")
- public void writePointcut() { }
- @Before("readPointcut()")
- public void read() {
- DataSourceContextHolder.slave();
- }
- @Before("writePointcut()")
- public void write() {
- DataSourceContextHolder.master();
- }
- @After("readPointcut()")
- public void readAfter() {
- DataSourceContextHolder.clear();
- }
- @After("writePointcut()")
- public void writeAfter() {
- DataSourceContextHolder.clear();
- }
- }
复制代码 7、.yml文件配置- spring:
- datasource:
- druid:
- master:
- username: root
- password: 12345678
- url: jdbc:mysql://192.168.10.15/zeroStart?characterEncoding=UTF-8&useSSL=true&requireSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
- driver-class-name: com.mysql.cj.jdbc.Driver
- type: com.zaxxer.hikari.HikariDataSource
- slave:
- username: root
- password: 12345678
- url: jdbc:mysql://localhost:3306/zeroStart?characterEncoding=UTF-8&useSSL=true&requireSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
- driver-class-name: com.mysql.cj.jdbc.Driver
- type: com.zaxxer.hikari.HikariDataSource
复制代码 8、引入的依赖- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
- <version>2.4.2</version>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-extension</artifactId>
- <version>${mybatis-plus.version}</version>
- </dependency>
-
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid</artifactId>
- <version>1.2.7</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>1.2.16</version>
- </dependency>
复制代码 配置到这就好了,业务代码就不展示了
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |