项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库。
最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法。
参考了两篇文章如下:
https://www.jb51.net/article/111840.htm
https://www.jb51.net/article/111842.htm
这两篇文章都对原理进行了分析,下面只写自己的实现过程其他不再叙述。
实现思路是:
第一步,实现动态切换数据源:配置两个DataSource,配置两个SqlSessionFactory指向两个不同的DataSource,两个SqlSessionFactory都用一个SqlSessionTemplate,同时重写Mybatis提供的SqlSessionTemplate类,最后配置Mybatis自动扫描。
第二步,利用aop切面,拦截dao层所有方法,因为dao层方法命名的特点,比如所有查询sql都是select开头,或者get开头等等,拦截这些方法,并把当前数据源切换至从库。
spring中配置如下:
主库数据源配置:
2
从库数据源配置:
主库SqlSessionFactory配置:
T selectOne(String statement) { return this.sqlSessionProxy. selectOne(statement); } public T selectOne(String statement, Object parameter) { return this.sqlSessionProxy. selectOne(statement, parameter); } public Map selectMap(String statement, String mapKey) { return this.sqlSessionProxy. selectMap(statement, mapKey); } public Map selectMap(String statement, Object parameter, String mapKey) { return this.sqlSessionProxy. selectMap(statement, parameter, mapKey); } public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.sqlSessionProxy. selectMap(statement, parameter, mapKey, rowBounds); } public List selectList(String statement) { return this.sqlSessionProxy. selectList(statement); } public List selectList(String statement, Object parameter) { return this.sqlSessionProxy. selectList(statement, parameter); } public List selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy. selectList(statement, parameter, rowBounds); } public void select(String statement, ResultHandler handler) { this.sqlSessionProxy.select(statement, handler); } public void select(String statement, Object parameter, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, handler); } public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); } public int insert(String statement) { return this.sqlSessionProxy.insert(statement); } public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } public int update(String statement) { return this.sqlSessionProxy.update(statement); } public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); } public int delete(String statement) { return this.sqlSessionProxy.delete(statement); } public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); } public T getMapper(Class type) { return getConfiguration().getMapper(type, this); } public void commit() { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void commit(boolean force) { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } public void rollback() { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void rollback(boolean force) { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } public void close() { throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); } public void clearCache() { this.sqlSessionProxy.clearCache(); } public Connection getConnection() { return this.sqlSessionProxy.getConnection(); } public List flushStatements() { return this.sqlSessionProxy.flushStatements(); } 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.sincetimes.slg.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 contextHolder = new ThreadLocal();
public static void setContextType(String contextType) {
contextHolder.set(contextType);
}
public static String getContextType() {
return contextHolder.get();
}
public static void clearContextType() {
contextHolder.remove();
}
}
最后就是写切面去对dao所有方法进行处理了,代码很简单如下:
package com.sincetimes.slg.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.sincetimes.slg.framework.util.SqlSessionContentHolder;
@Aspect
public class DynamicDataSourceAspect {
@Pointcut("execution( * com.sincetimes.slg.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 项目实现动态切换数据源实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对考高分网网站的支持!



