使用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读写分离的更多相关文章

  1. 使用Spring实现MySQL读写分离(转)

    使用Spring实现MySQL读写分离 为什么要进行读写分离 大量的JavaWeb应用做的是IO密集型任务, 数据库的压力较大, 需要分流 大量的应用场景, 是读多写少, 数据库读取的压力更大 一个很 ...

  2. 使用Spring实现MySQL读写分离

    1. 为什么要进行读写分离 大量的JavaWeb应用做的是IO密集型任务, 数据库的压力较大, 需要分流 大量的应用场景, 是读多写少, 数据库读取的压力更大 一个很自然的思路是使用一主多从的数据库集 ...

  3. spring+mybatis+mysql5.7实现读写分离,主从复制

    申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...

  4. MyBatis多数据源配置(读写分离)

    原文:http://blog.csdn.net/isea533/article/details/46815385 MyBatis多数据源配置(读写分离) 首先说明,本文的配置使用的最直接的方式,实际用 ...

  5. Spring AOP 实现读写分离

    原文地址:Spring AOP 实现读写分离 博客地址:http://www.extlight.com 一.前言 上一篇<MySQL 实现主从复制> 文章中介绍了 MySQL 主从复制的搭 ...

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

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

  7. 提高性能,MySQL 读写分离环境搭建

    这是松哥之前一个零散的笔记,整理出来分享给大伙! MySQL 读写分离在互联网项目中应该算是一个非常常见的需求了.受困于 Linux 和 MySQL 版本问题,很多人经常会搭建失败,今天松哥就给大伙举 ...

  8. 提高性能,MySQL 读写分离环境搭建(一)

    这是松哥之前一个零散的笔记,整理出来分享给大伙! MySQL 读写分离在互联网项目中应该算是一个非常常见的需求了.受困于 Linux 和 MySQL 版本问题,很多人经常会搭建失败,今天松哥就给大伙举 ...

  9. mysql读写分离(PHP类)

    mysql读写分离(PHP类) 博客分类: php mysql   自己实现了php的读写分离,并且不用修改程序 优点:实现了读写分离,不依赖服务器硬件配置,并且都是可以配置read服务器,无限扩展 ...

随机推荐

  1. thinkphp 模板赋值

    如果要在模板中输出变量,必须在在控制器中把变量传递给模板,系统提供了assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值. 大理石平台检定规程 $this->assign( ...

  2. thinkphp 命名范围

    在应用开发过程中,使用最多的操作还是数据查询操作,凭借ThinkPHP的连贯操作的特性,可以使得查询操作变得更优雅和清晰,命名范围功能则是给模型操作定义了一系列的封装,让你更方便的操作数据. 命名范围 ...

  3. String类型_static成员_动态内存分配_拷贝构造函数_const关键字_友元函数与友元类

    1:String类型 #include <iostream> using namespace std; int main() { //初始化方法 string s1 = "hel ...

  4. tar指令集合

    把常用的tar解压命令总结下,当作备忘: tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个, ...

  5. VIsualSVN server 安装及旧仓库导入

    安装参考: 1,  http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2407610.html SVN服务器搭建和使用(一) Subversi ...

  6. C++ 赋值函数为什么返回reference to *this?

    赋值操作为什么要返回 reference to *this? 要弄清这个问题之前,先了解函数的返回值类型:返回值类型,返回引用类型 返回值类型:返回的是一个对象的副本. test operator= ...

  7. hdu4764

    hdu4764bash博弈主要是找准必胜状态,以及好好理解题意.这里的必胜状态是n-1,虽然是写的数比上一个大1到k,但是相当于这个人拿1到k,然后是累加的效果 #include<iostrea ...

  8. Odoo的权限

    Odoo的权限的核心是权限组(res_groups).对每个权限组,可以设置权限组的菜单表示,对象表示,记录规则表示,字段表示. 1.菜单/对象级别 设置哪些人可以访问哪些菜单/对象,对象的访问权限包 ...

  9. List--列表合成

    1,基本规则是,一对中括号里面包含一个表达式,表达式里可以有for语句,还可以有分支的for或者if语句. 2,例如: 3,列表合成可以快速地合并多个列表. 例如: 当然还可以直接加:[1, 2, 3 ...

  10. Java校验时间段重叠

    1.需求 要求保存每一条数据的startTime.endTime的中间时间段是唯一的,跟其他数据时间段不能存在冲突 比如: (2019-03-01 -> 2019-03-03 )  (2019- ...