原理:通过自定义mybatis插件,拦截Executor的update和query方法,检查sql中有select就用读的库,其它的用写的库(如果有调用存储过程就另当别论了)

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }) })
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class }) })
public class RWDataSourceMybatisPlugin implements Interceptor { private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*"; private static final Map<String, RWDataSourceType> cacheMap = new ConcurrentHashMap<>(); @Override
public Object intercept(Invocation invocation) throws Throwable { Object[] objects = invocation.getArgs();
MappedStatement ms = (MappedStatement) objects[0]; RWDataSourceType dataSourceType = null; if ((dataSourceType = cacheMap.get(ms.getId())) == null) {
// 读方法
if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
// !selectKey 为自增id查询主键(SELECT LAST_INSERT_ID() )方法,使用主库
if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
dataSourceType = RWDataSourceType.WRITE;
} else {
BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);
String sql = boundSql.getSql().toLowerCase().replaceAll("[\\t\\n\\r]", " ");
if (sql.matches(REGEX)) {
dataSourceType = RWDataSourceType.WRITE;
} else {
dataSourceType = RWDataSourceType.READ;
}
}
} else {
dataSourceType = RWDataSourceType.WRITE;
}
cacheMap.put(ms.getId(), dataSourceType);
}
// 修改当前线程要选择的数据源的key
RWDataSourceHolder.putDataSource(dataSourceType); return invocation.proceed();
} @Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
}

附:

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

  spring boot加载插件方式

    @Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(dataSource);
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));
PageHelper pageHelper = new PageHelper();
Properties props = new Properties();
props.setProperty("reasonable", "true");
props.setProperty("supportMethodsArguments", "true");
props.setProperty("returnPageInfo", "check");
props.setProperty("params", "count=countSql");
pageHelper.setProperties(props);
fb.setPlugins(new Interceptor[] { pageHelper, new MybatisSqlInterceptor() });
return fb.getObject();
}

随便开发的一个记录sql的插件

package com.qmtt.config;

import java.sql.Connection;
import java.util.Properties; import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
public class MybatisSqlInterceptor implements Interceptor { @Override
public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
System.out.println(sql);
return invocation.proceed();
} @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) {
// String dialect = properties.getProperty("dialect");
}
}

mybatis通过插件方式实现读写分离的更多相关文章

  1. Spring Boot + Mybatis 多数据源配置实现读写分离

    本文来自网易云社区 作者:王超 应用场景:项目中有一些报表统计与查询功能,对数据实时性要求不高,因此考虑对报表的统计与查询去操作slave db,减少对master的压力. 根据网上多份资料测试发现总 ...

  2. SpringMVC4+MyBatis+SQL Server2014实现读写分离

    前言 基于mybatis的AbstractRoutingDataSource和Interceptor用拦截器的方式实现读写分离,根据MappedStatement的boundsql,查询sql的sel ...

  3. 基于Spring和Mybatis拦截器实现数据库操作读写分离

    首先需要配置好数据库的主从同步: 上一篇文章中有写到:https://www.cnblogs.com/xuyiqing/p/10647133.html 为什么要进行读写分离呢? 通常的Web应用大多数 ...

  4. Spring aop应用之实现数据库读写分离

    Spring加Mybatis实现MySQL数据库主从读写分离 ,实现的原理是配置了多套数据源,相应的sqlsessionfactory,transactionmanager和事务代理各配置了一套,如果 ...

  5. 重新学习Mysql数据13:Mysql主从复制,读写分离,分表分库策略与实践

    一.MySQL扩展具体的实现方式 随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量. 关于数据库的扩展主要包括:业务拆分.主从复制.读写分离.数据库分库 ...

  6. SSM/SSH框架的MySQL 读写分离实现的一种简单方法

    简介 MySQL已经是使用最为广泛的一种数据库,往往实际使用过程中,为实现高可用及高性能,项目会采用主丛复制的方式实现读写分离.MySQL本身支持复制,通过简单的配置即可实现一主多从的配置,具体实现可 ...

  7. MHA + Maxscale 数据库的高可用和读写分离

    MySQL 常见发行版本 MySQL 标准化.自动化部署 深入浅出MySQL备份与恢复 深入理解MySQL主从复制 MySQL构架设计与容量规划 MHA Maxscale MySQL 常见发行版本 M ...

  8. 安装oneproxy实现数据库的读写分离

    领导就给了两台数据库,做主从,在从上搭建oneproxy插件,实现读写分离,一直就听说oneproxy,今天打算用一下 先下载最新的版本wget http://www.onexsoft.cn/soft ...

  9. 基于Mycat实现读写分离

    随着应用的访问量并发量的增加,应用读写分离是很有必要的.当然应用要实现读写分离,首先数据库层要先做到主从配置,本人前一篇文章介绍了mysql数据库的主从配置方式即:<mysql数据库主从配置&g ...

随机推荐

  1. Introduce Null Object

    今天继续总结<重构>这本书中的一个重构手法,Introduce Null Object.写这个手法是因为它确实很巧妙,在实际编程中经常会遇到这种情况,前人总结出来了这么一个经典的手法,当然 ...

  2. file类简单操作

    file类可表示文件或文件夹 import java.io.File; import java.io.FilenameFilter; import java.io.IOException; impor ...

  3. shell脚本怎么调试

    shell是Linux系统上常用的一种脚本语言.一般从事web后台开发的从业者,都会用到shell,因此shell调试也是一项必备的技能.本文教你如何进行shell脚本调试. 工具/原料   Linu ...

  4. jsorder 第三方修改版 修正bug 增加总价

    我主要运用这个jsorder,修正了它的不足//1.0版本bug:刷新页面 无法增加或者删除原来添加的商品//1.1版本:修正了1.0版本  新增bug 能够修改原来的商品 但出现产品数量为0 仍然保 ...

  5. html5--6-5 CSS选择器2

    html5--6-5 CSS选择器2 实例 学习要点 掌握常用的CSS选择器 了解不太常用的CSS选择器 什么是选择器 当我们定义一条样式时候,这条样式会作用于网页当中的某些元素,所谓选择器就是样式作 ...

  6. virtualbox 复制虚拟机提示uuid is exists

    C:\Program Files\Oracle\VirtualBox>VBoxManage.exe internalcommands sethduuid D:毛毛草\virtual\ubuntu ...

  7. RxJava 参考文档

    /*************************************************************** * RxJava 参考文档 * 说明: * 最近无意中发现RxJava ...

  8. Linux 系统开机启动项清理

    一般情况下,常规用途的 Linux 发行版在开机启动时拉起各种相关服务进程,包括许多你可能无需使用的服务,例如蓝牙bluetooth.Avahi. 调制解调管理器ModemManager.ppp-dn ...

  9. robotframework:appium切换webview后,在第一个页面操作成功,跳转到第二个页面后,执行命令失败

    问题: 在用robot写手机淘宝app的自动化时,打开手机淘宝后,点击天猫国际,跳转到天猫国际页面,天猫国际页面是H5, 需要切换到对应的webview,切换到webview后,点击美妆菜单,跳转到美 ...

  10. windows8如何显示开始菜单

    按键盘上的Win+R 运行,输入regedit打开注册表 2 在注册表中找到HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Ex ...