Mybatis+Spring实现Mysql读写分离
使用spring AbstractRoutingDatasource实现多数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
//写数据源
private Object writeDataSource;
//读数据源
private Object readDataSource; @Override
public void afterPropertiesSet() {
Assert.isNull(writeDataSource, "Property 'writeDataSource' is required");
setDefaultTargetDataSource(writeDataSource);
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
targetDataSources.put(DBTypeEnum.WRITE.name(), writeDataSource);
if (null != readDataSource)
targetDataSources.put(DBTypeEnum.READ.name(), readDataSource);
setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
/**
* 获取当前使用那个数据源
*/
@Override
protected Object determineCurrentLookupKey() {
DBTypeEnum dataSource = DbContextHolder.getDbType();
if (null == dataSource || dataSource == DBTypeEnum.WRITE)
return DBTypeEnum.WRITE.name();
return DBTypeEnum.READ.name();
}
public Object getWriteDataSource() {
return writeDataSource;
}
public void setWriteDataSource(Object writeDataSource) {
this.writeDataSource = writeDataSource;
}
public Object getReadDataSource() {
return readDataSource;
}
public void setReadDataSource(Object readDataSource) {
this.readDataSource = readDataSource;
}
}
读写数据库类型
public enum DBTypeEnum {
WRITE, READ;
}
当前数据库配置上下文
public class DbContextHolder {
private static final ThreadLocal<DBTypeEnum> DATASOURCES = new ThreadLocal<DBTypeEnum>();
private DbContextHolder() {
}
/**
* 设置数据源
*
* @param dbType
*/
public static void setDbType(DBTypeEnum dbType) {
DATASOURCES.set(dbType.WRITE);
}
/*
* 获取当前数据源
* @return DBTypeEnum
* */
public static DBTypeEnum getDbType() {
return DATASOURCES.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
DATASOURCES.remove();
}
}
自定义事务管理器
public class DynamicDataSourceTransactionManager extends DataSourceTransactionManager {
/**
* 设置数据源
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
boolean readOnly = definition.isReadOnly();
if (readOnly)
DbContextHolder.setDbType(DBTypeEnum.READ);
DbContextHolder.setDbType(DBTypeEnum.WRITE);
super.doBegin(transaction, definition);
}
/**
* 清理本地线程数据源
*/
@Override
protected void doCleanupAfterCompletion(Object transaction) {
super.doCleanupAfterCompletion(transaction);
DbContextHolder.clearDbType();
}
}
mybatis插件(拦截器)
@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 DynamicPlugin implements Interceptor {
private static final Logger _log = LoggerFactory.getLogger(DynamicPlugin.class);
private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
private static final Map<String, DBTypeEnum> cachaMap = new HashMap<String, DBTypeEnum>();
@Override
public Object intercept(Invocation invocation) throws Throwable {
boolean isSynchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
if (!isSynchronizationActive) {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
DBTypeEnum dbTypeEnum = null;
if ((dbTypeEnum = cachaMap.get(ms.getId())) == null) {
//读方法
if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
//!selectKeu为自增id查询主键(SELECT LAST_INSERT_ID)方法,使用主库
if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX))
dbTypeEnum = DBTypeEnum.WRITE;
else {
BoundSql boundSql = ms.getSqlSource().getBoundSql(invocation.getArgs()[1]);
String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " ");
if (sql.matches(REGEX))
dbTypeEnum = DBTypeEnum.WRITE;
dbTypeEnum = DBTypeEnum.READ;
}
} else {
dbTypeEnum = DBTypeEnum.WRITE;
}
_log.warn("设置方法[{}] use [{}] Strategy, SqlCommandType [{}]..", ms.getId(), dbTypeEnum.name(), ms.getSqlCommandType().name());
cachaMap.put(ms.getId(), dbTypeEnum);
}
DbContextHolder.setDbType(dbTypeEnum);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
}
Mybatis+Spring实现Mysql读写分离的更多相关文章
- 使用Spring实现MySQL读写分离(转)
使用Spring实现MySQL读写分离 为什么要进行读写分离 大量的JavaWeb应用做的是IO密集型任务, 数据库的压力较大, 需要分流 大量的应用场景, 是读多写少, 数据库读取的压力更大 一个很 ...
- 使用Spring实现MySQL读写分离
1. 为什么要进行读写分离 大量的JavaWeb应用做的是IO密集型任务, 数据库的压力较大, 需要分流 大量的应用场景, 是读多写少, 数据库读取的压力更大 一个很自然的思路是使用一主多从的数据库集 ...
- spring+mybatis+mysql5.7实现读写分离,主从复制
申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...
- MyBatis多数据源配置(读写分离)
原文:http://blog.csdn.net/isea533/article/details/46815385 MyBatis多数据源配置(读写分离) 首先说明,本文的配置使用的最直接的方式,实际用 ...
- Spring AOP 实现读写分离
原文地址:Spring AOP 实现读写分离 博客地址:http://www.extlight.com 一.前言 上一篇<MySQL 实现主从复制> 文章中介绍了 MySQL 主从复制的搭 ...
- SSM/SSH框架的MySQL 读写分离实现的一种简单方法
简介 MySQL已经是使用最为广泛的一种数据库,往往实际使用过程中,为实现高可用及高性能,项目会采用主丛复制的方式实现读写分离.MySQL本身支持复制,通过简单的配置即可实现一主多从的配置,具体实现可 ...
- 提高性能,MySQL 读写分离环境搭建
这是松哥之前一个零散的笔记,整理出来分享给大伙! MySQL 读写分离在互联网项目中应该算是一个非常常见的需求了.受困于 Linux 和 MySQL 版本问题,很多人经常会搭建失败,今天松哥就给大伙举 ...
- 提高性能,MySQL 读写分离环境搭建(一)
这是松哥之前一个零散的笔记,整理出来分享给大伙! MySQL 读写分离在互联网项目中应该算是一个非常常见的需求了.受困于 Linux 和 MySQL 版本问题,很多人经常会搭建失败,今天松哥就给大伙举 ...
- mysql读写分离(PHP类)
mysql读写分离(PHP类) 博客分类: php mysql 自己实现了php的读写分离,并且不用修改程序 优点:实现了读写分离,不依赖服务器硬件配置,并且都是可以配置read服务器,无限扩展 ...
随机推荐
- JOIN方法也是连贯操作方法之一
JOIN方法也是连贯操作方法之一,用于根据两个或多个表中的列之间的关系,从这些表中查询数据. 大理石平台规格 join通常有下面几种类型,不同类型的join操作会影响返回的数据结果. INNER JO ...
- Python3.6爬虫+Djiago2.0+Mysql --运行djiago环境
1.安装djiago 模块 pip install Django --默认安装最新的 安装完成以后可以python -m pip list 查看模块是否安装 2.创建项目及app 及生成目录 备注 ...
- OpenCASCADE动画功能2
OpenCASCADE动画功能2 eryar@163.com OpenCASCADE是一个开发平台,主要提供三维曲面和实体建模.CAD数据交换及可视化等功能.OCCT最适用于开发三维建模CAD软件.加 ...
- Mybatis-SqlSessionFactoryBuilder,SessionFactory与SqlSession的并发控制
SqlSessionFactoryBuilder 这个类可以被实例化,使用和丢弃.一旦你创建了 SqlSessionFactory 后,这个类就不需要存在了.因此 SqlSessionFactoryB ...
- 路飞学城-Python爬虫集训-第三章
这个爬虫集训课第三章的作业讲得是Scrapy 课程主要是使用Scrapy + Redis实现分布式爬虫 惯例贴一下作业: Python爬虫可以使用Requests库来进行简单爬虫的编写,但是Reque ...
- 2019-4-16-C#-使用反射获取私有属性的方法
title author date CreateTime categories C# 使用反射获取私有属性的方法 lindexi 2019-4-16 10:13:3 +0800 2018-09-26 ...
- File- Linux必学的60个命令
1.作用 件内容判断文件类型,使用权限是所有用户. 2.格式 file通过探测文 file [options] 文件名 3.[options]主要参数 -v:在标准输出后显示版本信息,并且退出. -z ...
- Create STKNetDiskC Instance Error
关于Create STKNetDiskC Instance Error错误的解决方法 这个错误可能出现在: AM8 附件直接存网盘的时候 报错 解决方法步骤如下: 在出现该错误的机器上,先将AM及 m ...
- 【python之路44】tornado的用法 (二)
参考:https://www.cnblogs.com/sunshuhai/articles/6253815.html 一.代码目录构建 代码目录设置如下图: #!/usr/bin/env python ...
- Android Studio增加assets目录、raw目录
assets与res/raw不同 assets目录是Android的一种特殊目录,用于放置APP所需的固定文件,且该文件被打包到APK中时,不会被编码到二进制文件. Android还存在一种放置在re ...