• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Spring + Mybatis 项目实现动态切换数据源实例详解

java 搞代码 4年前 (2022-01-05) 24次浏览 已收录 0个评论

这篇文章主要介绍了Spring + Mybatis 项目实现动态切换数据源的相关资料,需要的朋友参考下吧

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

最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法。

参考了两篇文章如下:

https://www.gaodaima.com/article/111840.htm

https://www.gaodaima.com/article/111842.htm

这两篇文章都对原理进行了分析,下面只写自己的实现过程其他不再叙述。

实现思路是:

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

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

spring中配置如下:

主库数据源配置:

 2 

从库数据源配置:

  

主库SqlSessionFactory配置:

  

从库SqlSessionFactory配置:

  

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

   <map> </map>

配置Mybatis自动扫描dao

  

自己重写了SqlSessionTemplate代码如下:

 package com.sincetimes.slg.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.sincetimes.slg.framework.util.SqlSessionContentHolder; /** * * TODO 重写SqlSessionTemplate * @author ccg * @version 1.0 * Created 2017年4月21日 下午3: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 targetSqlSessionFactorys; private SqlSessionFactory defaultTargetSqlSessionFactory; public void setTargetSqlSessionFactorys(Map targetSqlSessionFactorys) { this.targetSqlSessionFactorys = targetSqlSessionFactorys; } public Map 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 selectOne(String statement) { return this.sqlSessionProxy. selectOne(statement); } /** * {@inheritDoc} */ public  T selectOne(String statement, Object parameter) { return this.sqlSessionProxy. selectOne(statement, parameter); } /** * {@inheritDoc} */ public  Map selectMap(String statement, String mapKey) { return this.sqlSessionProxy. selectMap(statement, mapKey); } /** * {@inheritDoc} */ public  Map selectMap(String statement, Object parameter, String mapKey) { return this.sqlSessionProxy. selectMap(statement, parameter, mapKey); } /** * {@inheritDoc} */ public  Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.sqlSessionProxy. selectMap(statement, parameter, mapKey, rowBounds); } /** * {@inheritDoc} */ public  List selectList(String statement) { return this.sqlSessionProxy. selectList(statement); } /** * {@inheritDoc} */ public  List selectList(String statement, Object parameter) { return this.sqlSessionProxy. selectList(statement, parameter); } /** * {@inheritDoc} */ public  List selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy. 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 getMapper(Class 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 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 instanc<strong style="color:transparent">来源gaodai#ma#com搞@@代~&码*网</strong>eof 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 项目实现动态切换数据源实例详解的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Spring + Mybatis 项目实现动态切换数据源实例详解

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址