带你了解mybatis如何实现读写分离

网友投稿 278 2022-12-22

带你了解mybatis如何实现读写分离

目录1、spring aop实现2、mybatis-plus的实现方式总结

1、spring aop实现

首先application-test.yml增加如下数据源的配置

spring:

datasource:

master:

jdbc-url: jdbc:mysql://master域名:3306/test

username: root

password: 123456

driver-class-name: com.mysql.jdbc.Driver

slave1:

jdbc-url: jdbc:mysql://slave域名:3306/test

username: root # 只读账户

password: 123456

driver-class-name: com.mysql.jdbc.Driver

slave2:

jdbc-url: jdbc:mysql://slave域名:3306/test

username: root # 只读账户

password: 123456

driver-class-name: com.mysql.jdbc.Driver

package com.cjs.example.enums;

public enuWDRJHhVrm DBTypeEnum {

MASTER, SLAVE1, SLAVE2;

}

定义ThreadLocal上下文,将当前线程的数据源进行动态修改

public class DBContextHolder {

private static volatile ThreadLocal contextHolder = new ThreadLocal<>();

public static synchronized void set(DBTypeEnum dbType) {

contextHolder.set(dbType);

}

public static synchronized DBTypeEnum get() {

return contextHolder.get();

}

public static void master() {

set(DBTypeEnum.MASTER);

}

public static void slave() {

set(DBTypeEnum.SLAVE1);

}

public static void slave2(){ set(DBTypeEnum.SLAVE2); }

// 清除数据源名

public static void clearDB() {

contextHolder.remove();

}

}

重写mybatis数据源路由接口,在此修改数据源为我们上一块代码设置的上下文的数据源

public class MyRoutingDataSource extends AbstractRoutingDataSource {

@Nullable

@Override

protected Object determineCurrentLookupKey() {

DBTypeEnum dbTypeEnum=DBContextHolder.get();

return dbTypeEnum;

}

}

将yml配置的多数据源手动指定注入

@Configuration

public class DataSourceConfig {

@Bean

@ConfigurationProperties("spring.datasource.master")

public DataSource masterDataSource() {

return DataSourceBuilder.create().build();

}

@Bean

@ConfigurationProperties("spring.datasource.slave1")

public DataSource slave1DataSource() {

return DataSourceBuilder.create().build();

}

@Bean

public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,

@Qualifier("slave1DataSource") DataSource slave1DataSource) {

Map targetDataSources = new HashMap<>();

targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);

targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);

MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();

myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);

myRoutingDataSource.setTargetDataSources(targetDataSources);

return myRoutingDataSource;

}

}

sqlsession注入以上我们配置的datasource路由

@EnableTransactionManagement

@Configuration

@Import({TableSegInterceptor.class})

public class MyBatisConfig {

@Resource(name = "myRoutingDataSource")

private DataSource myRoutingDataSource;

@Autowired

private MybatisConfigProperty mybatisConfigProperty;

@Autowired

private TableSegInterceptor tableSegInterceptor;

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

sqlSessionFactoryBean.setDataSource(myRoutingDataSource);

// SpringBoot项目集成mybatis打包为jar运行时setTypeAliasesPackage无效解决

VFS.addImplClass(SpringBootVFS.class);

sqlSessionFactoryBean.setMapperLocations(

new PathMatchingResourcePatternResolver().getResources(mybatisConfigProperty.getMapperLocations()));

sqlSessionFactoryBean.setTypeAliasesPackage(mybatisConfigProperty.getTypeAliasesPackage());

sqlSessionFactoryBean.setConfigLocation(

new PathMatchingResourcePatternResolver().getResource(mybatisConfigProperty.getConfigLocation()));

sqlSessionFactoryBean.setPlugins(new Interceptor[]{tableSegInterceptor});

return sqlSessionFactoryBean.getObject();

}

@Bean

public PlatformTransactionManager platformTransactionManager() {

return new DataSourceTransactionManager(myRoutingDataSource);

}

}

spring aop拦截指定前缀的service方法,并设置对应所属的上下文

@Aspect

@Component

public class DataSourceAop {

@Pointcut("!@annotation(com.ask.student.interceptor.annotation.Master) " +

"&& (execution(* com.ask.student.service..*.select*(..)) " +

"|| execution(* com.ask.student.service..*.get*(..))" +

"|| execution(* com.ask.student.service..*.find*(..))" +

")")

public void readPointcut() {

}

@Pointcut("@annotation(com.ask.student.interceptor.annotation.Master) " +

"|| execution(* com.ask.student.service..*.insert*(..)) " +

"|| execution(* com.ask.student.service..*.clean*(..)) " +

"|| execution(* com.ask.student.service..*.reset*(..)) " +

"|| execution(* com.ask.student.service..*.add*(..)) " +

"|| execution(* com.ask.student.service..*.update*(..)) " +

"|| execution(* com.ask.student.service..*.edit*(..)) " +

"|| execution(* com.ask.student.service..*.delete*(..)) " +

"|| execution(* com.ask.student.service..*.remove*(..))")

public void writePointcut() {

}

@Before("readPointcut()")

public void read() {

DBContextHolder.slave();

}

@Before("writePointcut()")

public void write() {

DBContextHolder.master();

}

@After("readPointcut()||writePointcut()")

public void afterSwitchDS(){

DBContextHolder.clearDB();

}

}

以上最后一个方法的作用,在拦截器中获取后及时清除避免导致来回切换当前线程变量延迟问题导致某些操作的数据源错误

DBContextHolder.clearDB();

@After("readPointcut()||writePointcut()")

public void afterSwitchDS(){

DBContextHolder.clearDB();

}

2、mybatis-plus的实现方式

这个方式配置简单,代码少,很多事情mybatis-plus都已经做好了,推荐使用

yml配置如下

datasource:

dynamic:

primary: master #设置默认的数据源或者数据源组,默认值即为master

strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.

datasource:

master:

url: jdbc:mysql://xxx:3306/db0?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai

username: admin

password: 123456

driver-class-name: com.mysql.cj.jdbc.Driver

type: com.zaxxer.hikari.HikariDataSource

hikari:

minimum-idle: 5

maximum-pool-size: 15

auto-commit: true

idle-timeout: 30000

pool-name: springHikariCP

max-lifetime: 1800000

connection-timeout: 30000

connection-test-query: SELECT 1

slave1:

url: jdbc:mysql://xxx:3306/db2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai

username: admin

password: 123456

driver-class-name: com.mysql.cj.jdbc.Driver

type: com.zaxxer.hikari.HikariDataSource

hikari:

minimum-idle: 5

maximum-pool-size: 15

auto-commit: true

idle-timeout: 30000

pool-name: springHikariCP

max-lifetime: 1800000

connection-timeout: 30000

connection-test-query: SELECT 1

slave2:

url: jdbc:mysql://xxx:3306/db3?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai

username: admin

password: 123456

driver-class-name: com.mysql.cj.jdbc.Driver

type: com.zaxxer.hikari.HikariDataSource

hikari:

minimum-idle: 5

maximum-pool-size: 15

auto-commit: true

idle-timeout: 30000

pool-name: springHikariCP

max-lifetime: 1800000

connection-timeout: 30000

connection-test-query: SELECT 1

使用起来非常简单,只需要加上这个master的注解即可

@Override

@DS("master")

public DestMedia getOneByCodeFromEpg(String code) {

QueryWrapper queryWrapper = new QueryWrapper();

queryWrapper.eq("code", code);

return super.getOne(queryWrapper);

}

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java中的基础运算符
下一篇:springboot自动装配的源码与流程图
相关文章

 发表评论

暂时没有评论,来抢沙发吧~