带你了解mybatis如何实现读写分离
作者:alexander137 发布时间:2023-07-29 14:26:28
标签:mybatis,读写分离
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 enum DBTypeEnum {
MASTER, SLAVE1, SLAVE2;
}
定义ThreadLocal上下文,将当前线程的数据源进行动态修改
public class DBContextHolder {
private static volatile ThreadLocal<DBTypeEnum> 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<Object, Object> 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);
}
来源:https://blog.csdn.net/u013309797/article/details/119254805


猜你喜欢
- Java 8 , Lambda + foreach 语法糖, 写起来非常的 cleanpublic static void main(Str
- 本文实例讲述了C#数据结构之双向链表(DbLinkList)。分享给大家供大家参考,具体如下:这是继上一篇《C#数据结构之单链表(LinkL
- String 不是简单类型,而是一个类,它被用来表示字符序列。字符本身符合 Unicode 标准,其初始化方式有两种。如:String gr
- 引入pom<?xml version="1.0" encoding="UTF-8"?>&
- 管理fragment的生命周期有些像管理activity的生命周期。Fragment可以生存在三种状态:Resumed:Fragment在一
- 引言之前写了一篇关于 TraceId 的文章:为全局请求添加 TraceId ,看日志再也不懵逼今天就接着 TraceId 做一些优化,如果
- 一、本地仓库初始化与远程仓库推送操作Idea 基本环境配置Github 配置Git 执行文件目录指定创建工程git02创建本地仓库并提交项目
- 这篇文章主要介绍了简单了解Java方法的定义和使用实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 注解从java5开始加入这一特性,发展到现在已然是遍地开花,在很多框架中得到了广泛的使用,用来简化程序中的配置。那充满争议的类型注解究竟是什
- 本文实例介绍了Android实现ImageView图片双击放大及缩小的相关技巧,分享给大家供大家参考,具体内容如下public class
- 前言我们平时在开发中,难免会遇到一些比较特殊的需求,就比如我们这篇文章的主题,一个关于圆弧滑动的,一般是比较少见的。其实在遇到这些东西时,不
- 前言:回顾上一节服务器配置的内容,我们已经可以自己完成公众号服务器的配置。配置完成之后,我们就可以通过调用的方式,完成对消息管理的处理。当用
- java 泛型方法:泛型是什么意思在这就不多说了,而Java中泛型类的定义也比较简单,例如:public class Test
- 昨天使用mybatis-plus。使用自动填充后发现了两个问题。一个是填充数据为null,一个是当使用了mybatis-plus的乐观锁,自
- 一、项目简述功能包括(管理员和游客角色):1:用户及主要操作功能 游客可以浏览网站的主页,但是需要注册为会员后部分小 说才能对网络小说进免费
- 实现比较两个List之间的差异,包括获取两List的差集,交集,并集(不去重&去重)的API解法和优化解法的解决方案。求差集/**
- 题目给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: "abcabcbb&qu
- 这里分享下我在日常开发中对 Gradle 的常用配置规则一、版本号配置当项目逐渐演进的过程中,主工程依赖的 Module 可能会越来越多,此
- 本文为大家分享了Android仿小度语音助手的贝塞尔曲线动画,供大家参考,具体内容如下废话不多说,看下面的动图,和百度的还是有点点差别,我也
- 前言提前说明下:(该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并