项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库。

实现思路是:

第一步,实现动态切换数据源:配置两个DataSource,配置两个SqlSessionFactory指向两个不同的DataSource,两个SqlSessionFactory都用一个SqlSessionTemplate,同时重写Mybatis提供的SqlSessionTemplate类,最后配置Mybatis自动扫描。

第二步,利用aop切面,拦截dao层所有方法,因为dao层方法命名的特点,比如所有查询sql都是select开头,或者get开头等等,拦截这些方法,并把当前数据源切换至从库。

spring中配置如下:

主库数据源配置:

<bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${master_mysql_jdbc_driver}" />
<property name="jdbcUrl" value="${master_mysql_jdbc_url}" />
<property name="user" value="${master_mysql_jdbc_user}" />
<property name="password" value="${master_mysql_jdbc_password}" />
</bean>

从库数据源配置:

<bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${slave_mysql_jdbc_driver}" />
<property name="jdbcUrl" value="${slave_mysql_jdbc_url}" />
<property name="user" value="${slave_mysql_jdbc_user}" />
<property name="password" value="${slave_mysql_jdbc_password}" />
</bean>

主库SqlSessionFactory配置:

<bean id="masterSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="masterDataSource" />
<property name="mapperLocations" value="classpath:com/bhrk/dao/*.xml"/>
</bean>

从库SqlSessionFactory配置:

<bean id="slaveSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="slaveDataSource" />
<property name="mapperLocations" value="classpath:com/bhrk/dao/*.xml"/>
</bean>

两个SqlSessionFactory使用同一个SqlSessionTemplate配置:

<bean id="DynamicSqlSessionTemplate" class="com.bhrk.framework.core.DynamicSqlSessionTemplate">
<constructor-arg index="0" ref="masterSqlSessionFactory" />
<property name="targetSqlSessionFactorys">
<map>
<entry value-ref="masterSqlSessionFactory" key="master"/>
<entry value-ref="slaveSqlSessionFactory" key="slave"/>
</map>
</property>
</bean>

重写SqlSessionTemplate代码如下:

package com.bhrk.framework.core;

import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map; import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert; import com.bhrk.framework.util.SqlSessionContentHolder; /**
*
* TODO 重写SqlSessionTemplate
* @author bhrk
* @version 1.0
* Created 2017年11月21日 下午1:15:15
*/
public class DynamicSqlSessionTemplate extends SqlSessionTemplate { private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator; private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
private SqlSessionFactory defaultTargetSqlSessionFactory; public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
this.targetSqlSessionFactorys = targetSqlSessionFactorys;
} public Map<Object, SqlSessionFactory> getTargetSqlSessionFactorys(){
return targetSqlSessionFactorys;
} public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
} public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
} public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
.getEnvironment().getDataSource(), true));
} public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) { super(sqlSessionFactory, executorType, exceptionTranslator); this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor()); this.defaultTargetSqlSessionFactory = sqlSessionFactory;
} @Override
public SqlSessionFactory getSqlSessionFactory() { SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(SqlSessionContentHolder.getContextType());
if (targetSqlSessionFactory != null) {
return targetSqlSessionFactory;
} else if (defaultTargetSqlSessionFactory != null) {
return defaultTargetSqlSessionFactory;
} else {
Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
}
return this.sqlSessionFactory;
} @Override
public Configuration getConfiguration() {
return this.getSqlSessionFactory().getConfiguration();
} public ExecutorType getExecutorType() {
return this.executorType;
} public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
} /**
* {@inheritDoc}
*/
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.<T> selectOne(statement);
} /**
* {@inheritDoc}
*/
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T> selectOne(statement, parameter);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
} /**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.<E> selectList(statement);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E> selectList(statement, parameter);
} /**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
} /**
* {@inheritDoc}
*/
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
} /**
* {@inheritDoc}
*/
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
} /**
* {@inheritDoc}
*/
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
} /**
* {@inheritDoc}
*/
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
} /**
* {@inheritDoc}
*/
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
} /**
* {@inheritDoc}
*/
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
} /**
* {@inheritDoc}
*/
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
} /**
* {@inheritDoc}
*/
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
} /**
* {@inheritDoc}
*/
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
} /**
* {@inheritDoc}
*/
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
} /**
* {@inheritDoc}
*/
public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
} /**
* {@inheritDoc}
*/
public void clearCache() {
this.sqlSessionProxy.clearCache();
} /**
* {@inheritDoc}
*/
public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
} /**
* {@inheritDoc}
* @since 1.0.2
*/
public List<BatchResult> flushStatements() {
return this.sqlSessionProxy.flushStatements();
} /**
* Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
* unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
* the {@code PersistenceExceptionTranslator}.
*/
private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = getSqlSession(
DynamicSqlSessionTemplate.this.getSqlSessionFactory(),
DynamicSqlSessionTemplate.this.executorType,
DynamicSqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory())) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (DynamicSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
}
}
} }

SqlSessionContentHolder类代码如下:

package com.bhrk.framework.util;

public abstract class SqlSessionContentHolder {

    public final static String SESSION_FACTORY_MASTER = "master";
public final static String SESSION_FACTORY_SLAVE = "slave"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setContextType(String contextType) {
contextHolder.set(contextType);
} public static String getContextType() {
return contextHolder.get();
} public static void clearContextType() {
contextHolder.remove();
}
}

最后就是写切面去对dao所有方法进行处理了,代码很简单如下:

<h1   style="color:red">一定要引入Aspect相关的包</h1>

package com.bhrk.framework.core;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut; import com.bhrk.framework.util.SqlSessionContentHolder; @Aspect
public class DynamicDataSourceAspect { @Pointcut("execution( * com.bhrk.dao.*.*(..))")
public void pointCut(){ }
@Before("pointCut()")
public void before(JoinPoint jp){
String methodName = jp.getSignature().getName();
//dao方法查询走从库
if(methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("list")){
SqlSessionContentHolder.setContextType(SqlSessionContentHolder.SESSION_FACTORY_SLAVE);
}else{
SqlSessionContentHolder.setContextType(SqlSessionContentHolder.SESSION_FACTORY_MASTER);
}
} }

Spring + Mybatis 读写分离的更多相关文章

  1. mybatis读写分离

    mybatis读写分离实现方式有很多种,当然如果没有太过复杂的处理,可以使用阿里云数据库自带的读写分离连接,那样会更加简洁.本文主要对mybatis实现读写分离.主要的实现方式有一下四种: 方案1 通 ...

  2. java 使用spring实现读写分离

    最近上线的项目中数据库数据已经临近饱和,最大的一张表数据已经接近3000W,百万数据的表也有几张,项目要求读数据(select)时间不能超过0.05秒,但实际情况已经不符合要求,explain建立索引 ...

  3. ssm maven spring AOP读写分离

    ssm maven spring AOP读写分离 总体流程 配置最开始写在pom.xml文件,解析到数据库配置文件,再解析到spring配置文件. 自定义注解DataSource:通过这个注解并且在s ...

  4. spring实现读写分离

    (转自:http://www.cnblogs.com/surge/p/3582248.html) 现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数 ...

  5. Spring-Blog:个人博客(一)-Mybatis 读写分离

    概述: 2018,在平(tou)静(lan)了一段时间后,开始找点事情来做.这一次准备开发一个个人博客,在开发过程之中完善一下自己的技术.本系列博客只会提出一些比较有价值的技术思路,不会像写流水账一样 ...

  6. 搭建 springboot 2.0 mybatis 读写分离 配置区分不同环境

    最近公司打算使用springboot2.0, springboot支持HTTP/2,所以提前先搭建一下环境.网上很多都在springboot1.5实现的,所以还是有些差异的.接下来咱们一块看一下. 文 ...

  7. Spring 数据库读写分离

    读写分离常见有俩种方式 1 第一种方式比较常用就是定义2个数据库连接,一个是Master,另一个是Slave.更新数据时我们取Master,查询数据时取Slave.太过简单不做介绍. 2 第二种方数据 ...

  8. SpringBoot Mybatis 读写分离配置(山东数漫江湖)

    为什么需要读写分离 当项目越来越大和并发越来大的情况下,单个数据库服务器的压力肯定也是越来越大,最终演变成数据库成为性能的瓶颈,而且当数据越来越多时,查询也更加耗费时间,当然数据库数据过大时,可以采用 ...

  9. Mysql8.0主从复制搭建,shardingsphere+springboot+mybatis读写分离

    1.安装mysql8.0 首先需要在192.167.3.171上安装JDK. 下载mysql安装包,https://dev.mysql.com/downloads/,找到以下页面下载. 下载后放到li ...

随机推荐

  1. 【个人博客作业II】代码复审结果

    [代码复审结果] General Does the code work? Does it perform its intended function, the logic is correct etc ...

  2. wuziqi

          五子棋结对人崔保雪的博客连接http://www.cnblogs.com/nuoxiaomi/ 题目简介 我们实现了一个五子棋的软件,该软件由初始化模块.下棋操作模块.人机对战模块.人人对 ...

  3. Pyhton语句

    一.if条件语句 1.python 并不支持 switch 语句 num = 5 if num == 3: # 判断num的值 print 'boss' elif num == 2: print 'u ...

  4. WebService相关

    1.WebService 之 WSDL文件 讲解 2.WSDL样式详解 3.IntelliJ IDEA 开发 WebService 4.浅谈WebService的调用 5.用IDEA8快速开发WebS ...

  5. SSM三大框架整合配置(Spring+SpringMVC+MyBatis)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...

  6. 学习笔记之form表单

    form表单提交的数据 是字典类型 这样 方便在create时候 直接解压

  7. Spark_RDD之简单Java函数接口

    函数名 实现的方法 用途 Function<T, R> R call(T) 接收一个输入值并返回一个输出值,用于类似 map() 和filter() 等操作中 Function2<T ...

  8. BZOJ2213[Poi2011]Difference——DP

    题目描述 A word consisting of lower-case letters of the English alphabet ('a'-'z') is given. We would li ...

  9. json_encode转化为数组时带下标

    //错误 data[$i]=[date("y-m-d H:i:s"),数据查询] //正确 data[]=[date("y-m-d H:i:s"),数据查询] ...

  10. codeforces 797B

    B. Odd sum time limit per test 1 second memory limit per test 256 megabytes input standard input out ...