• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Java > 关于Spring3 + Mybatis3整合时多数据源动态切换的问题

关于Spring3 + Mybatis3整合时多数据源动态切换的问题

作者:兔子党-大胡子 字体:[增加 减小] 来源:互联网 时间:2017-05-28

兔子党-大胡子 通过本文主要向大家介绍了spring3,spring3 jar包下载,spring3下载,spring3和spring4,spring3 mvc等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

以前的项目经历中,基本上都是spring + hibernate + Spring JDBC这种组合用的多。至于MyBatis,也就这个项目才开始试用,闲话不多说,进入正题。

以前的这种框架组合中,动态数据源切换可谓已经非常成熟了,网上也有非常多的博客介绍,都是继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法。具体做法就不在此废话了。

所以当项目中碰到这个问题,我几乎想都没有想,就采用了这种做法,但是一测试,一点反应都没有。当时觉得不可能,于是断点,加log调试,发现determineCurrentLookupKey()根本没有调用。 

为什么列? 这不可能啊。静下心来,仔细想想,才想到一个关键的问题: Mybatis整合Spring,而不是Spring整合的Mybatis! 直觉告诉我,问题就出在这里。

于是花时间去研究一下mybatis-spring.jar 这个包,发现有SqlSession这东西,很本能的就注意到了这一块,然后大致看一下他的一些实现类。于是就发现了他的实现类里面有一个内部类SqlSessionInterceptor(研究过程就不多说了,毕竟是个痛苦的过程)

好吧,这个类的作用列,就是产生sessionProxy。关键代码如下

final SqlSession sqlSession = getSqlSession( 
 SqlSessionTemplate.this.sqlSessionFactory, 
 SqlSessionTemplate.this.executorType, 
 SqlSessionTemplate.this.exceptionTranslator); 
</div>

这个sqlSessionFactory 我们就很眼熟啦,是我们在spring配置文件中配了的,是吧,也就是说这东西是直接从我们配置文件中读进来,但这东西,就关联了Datasource。所以就想到,如果能把这东西,做到动态,那么数据源切换,也就动态了。

于是第一反应就是写了一个类,然后在里面定义一个Map,用来存放多个SqlSessionFactory,并采用Setter方法进行属性注入。

public class EjsSqlSessionTemplate extends SqlSessionTemplate { 
 
 private Map<String, SqlSessionFactory> targetSqlSessionFactory = new HashMap<String, SqlSessionFactory>(); 
 public void setTargetSqlSessionFactory(Map<String, SqlSessionFactory> targetSqlSessionFactory) { 
  this.targetSqlSessionFactory = targetSqlSessionFactory; 
 } 
</div>

所以Spring的配置文件就变成了这样:

<bean id="sqlSessionTemplate" class="com.ejushang.spider.datasource.EjsSqlSessionTemplate"> 
  <constructor-arg ref="sqlSessionFactory" /> 
  <property name="targetSqlSessionFactory"> 
   <map> 
    <entry value-ref="sqlSessionFactory" key="spider"/> 
    <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/> 
   </map> 
  </property> 
 </bean> 
 <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 
  <property name="basePackage" value="com.foo.bar.**.mapper*" /> 
  <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> 
 </bean> 
</div>

那么这个思想是那里来的列? 当然就是借鉴了Spring的动态数据源的思想啦,对比一下Spring动态数据源的配置,看看是不是差不多?

然后重写了个关键的方法:

/** 
  * 重写得到SqlSessionFactory的方法 
  * @return 
  */ 
 @Override 
 public SqlSessionFactory getSqlSessionFactory() { 
 
  SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey()); 
  if (targetSqlSessionFactory != null) { 
   return targetSqlSessionFactory; 
  } else if ( this.getSqlSessionFactory() != null) { 
   return this.getSqlSessionFactory(); 
  } 
  throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least"); 
 } 
</div>

而SqlSessionContextHolder就很简单,就是一个ThreadLocal的思想

public class SqlSessionContextHolder { 
 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 
 private static Logger logger = LoggerFactory.getLogger(SqlSessionContextHolder.class); 
 public static void setSessionFactoryKey(String dataSourceKey) { 
  contextHolder.set(dataSourceKey); 
 } 
 public static String getDataSourceKey() { 
  String key = contextHolder.get(); 
  logger.info("当前线程Thread:"+Thread.currentThread().getName()+" 当前的数据源 key is "+ key); 
  return key; 
 } 
} 
</div>

博主信心满满就开始测试了。。结果发现不行,切换不过来,始终都是绑定的是构造函数中的那个默认的sqlSessionFactory,当时因为看了一天源码,头也有点晕。其实为什么列?

看看我们产生sessionProxy的地方代码,他的sqlSessionFactory是直接从构造函数来拿的。而构造函数中的sqlSessionFactory在spring容器启动时,就已经初始化好了,这点也可以从我们Spring配置文件中得到证实。

那这个问题,怎么解决列? 于是博主便想重写那个sqlSessionInterceptor。 擦,问题就来了,这个类是private的,没办法重写啊。于是博主又只能在自己的EjsSqlSessionTemplate类中,也定义了一个内部类,把源码中的代码都copy过来,唯一不同的就是我不是读取构造函数中的sqlSessionFactory.而是每次都去调用 getSqlSessionFactory()方法。代码如下:

final SqlSession sqlSession = getSqlSession(    
EjsSqlSessionTemplate.this.getSqlSessionFactory(),    
EjsSqlSessionTemplate.this.getExecutorType(),     
EjsSqlSessionTemplate.this.getPersistenceExceptionTranslator()); 
</div>

再试,发现还是不行,再找原因,又回归到了刚才那个问题。因为我没有重写SqlSessionTemplate的构造函数,而sqlSessionProxy是在构函数中初始化的,代码如下:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, 
 PersistenceExceptionTranslator exceptionTranslator) { 
 notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); 
 notNull(executorType, "Property 'executorType' is required"); 
 this.sqlSessionFactory = sqlSessionFactory; 
 this.executorType = executorType; 
 this.exceptionTranslator = exceptionTranslator; 
 this.sqlSessionProxy = (SqlSession) newProxyInstance( 
  SqlSessionFactory.class.getClassLoader(), 
  new Class[] { SqlSession.class }, 
  new SqlSessionInterceptor()); 
} 
</div>

而SqlSessionInterceptor()这东西都是private。 所以父类压根就不会加载我写的那个SqlSessionInterceptor()。所以问题就出在这,那好吧,博主又重写构函数

public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { 
  super(getSqlSessionFactory(), executorType, exceptionTranslator); 
 } 
</div>

很明显这段代码是编译不通过的,构造函数中,怎么可能调用类实例方法列?  那怎么办列? 又只有把父类的构造函数copy过来,那问题又有了,这些成员属性又没有。那又只得把他们也搬过来。。  后来,这个动态数据数据源的功能,终于完成了。

--------------------------------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------整个完整的代码如下:

1、重写SqlSessionTemplate (重写的过程已经在上面分析过了)

public class EjsSqlSessionTemplate extends SqlSessionTemplate { 
 private final SqlSessionFactory sqlSessionFactory; 
 private final ExecutorType executorType; 
 private final SqlSession sqlSessionProxy; 
 private final PersistenceExceptionTranslator exceptionTranslator; 
 private Map<Object, SqlSessionFactory> targetSqlSessionFactory; 
 public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) { 
  this.targetSqlSessionFactory = targetSqlSessionFactory; 
 } 
 public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { 
  this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); 
 } 
 public EjsSqlSessionTe



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程
  • 关于Spring3 + Mybatis3整合时多数据源动态切换的问题
  • 详解在Spring3中使用注解(@Scheduled)创建计划任务
  • 详解使用Spring3 实现用户登录以及权限认证
  • 详解spring boot Websocket使用笔记
  • Spring3 MVC请求参数获取的几种方法小结
  • Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程
  • 关于Spring3 + Mybatis3整合时多数据源动态切换的问题

相关文章

  • 2017-05-28java基于dom4j包实现对XML解析的方法
  • 2017-05-28Java导出oracle表结构实例详解
  • 2017-05-28Struts2 控制文件上传下载功能实例代码
  • 2017-05-28log4j的Appenders配置方法
  • 2017-05-28浅谈Java中注解Annotation的定义、使用、解析
  • 2017-05-28java 基础之final、finally和finalize的区别
  • 2017-05-28基于Java的正则表达式
  • 2017-05-28java web开发中大量数据导出Excel超时(504)问题解决
  • 2017-08-25java解析压缩文件.zip,获取文件夹下的数据
  • 2017-05-28java实现微信H5支付方法详解

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解
    • java的线程池框架及线程池的原理
    • 详解Spring 基于 Aspect 注解的增强实现
    • Java线程同步、同步方法实例详解
    • Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)
    • Spring Boot JPA访问Mysql示例
    • Java 中 String,StringBuffer 和 StringBuilder 的区别及用法
    • Spring整合Redis完整实例代码
    • SpringBoot添加Email发送功能及常见异常详解
    • Java中LinkedList详解和使用示例_动力节点Java学院整理

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有