在使用Mybatis与Spring集成的时候我们用到了SqlSessionTemplate 这个类。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

通过源码我们何以看到 SqlSessionTemplate 实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate 来代理以往的DefailtSqlSession完成对数据库的操作,但是DefailtSqlSession这个类不是线程安全的,所以这个类不可以被设置成单例模式的。

如果是常规开发模式 我们每次在使用DefailtSqlSession的时候都从SqlSessionFactory当中获取一个就可以了。但是与Spring集成以后,Spring提供了一个全局唯一的SqlSessionTemplate示例 来完成DefailtSqlSession的功能,问题就是:无论是多个dao使用一个SqlSessionTemplate,还是一个dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession,当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让我们一起来分析一下。

(1)首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
2 PersistenceExceptionTranslator exceptionTranslator) {
3
4 notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
5 notNull(executorType, "Property 'executorType' is required");
6
7 this.sqlSessionFactory = sqlSessionFactory;
8 this.executorType = executorType;
9 this.exceptionTranslator = exceptionTranslator;
10 this.sqlSessionProxy = (SqlSession) newProxyInstance(
11 SqlSessionFactory.class.getClassLoader(),
12 new Class[] { SqlSession.class },
13 new SqlSessionInterceptor());
14 }

核心代码就在 SqlSessionInterceptor的invoke方法当中。

private class SqlSessionInterceptor implements InvocationHandler {
2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
3 //获取SqlSession(这个SqlSession才是真正使用的,它不是线程安全的)
4 //这个方法可以根据Spring的事物上下文来获取事物范围内的sqlSession
5 //一会我们在分析这个方法
6 final SqlSession sqlSession = getSqlSession(
7 SqlSessionTemplate.this.sqlSessionFactory,
8 SqlSessionTemplate.this.executorType,
9 SqlSessionTemplate.this.exceptionTranslator);
10 try {
11 //调用真实SqlSession的方法
12 Object result = method.invoke(sqlSession, args);
13 //然后判断一下当前的sqlSession是否被Spring托管 如果未被Spring托管则自动commit
14 if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
15 // force commit even on non-dirty sessions because some databases require
16 // a commit/rollback before calling close()
17 sqlSession.commit(true);
18 }
19 //返回执行结果
20 return result;
21 } catch (Throwable t) {
22 //如果出现异常则根据情况转换后抛出
23 Throwable unwrapped = unwrapThrowable(t);
24 if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
25 Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
26 if (translated != null) {
27 unwrapped = translated;
28 }
29 }
30 throw unwrapped;
31 } finally {
32 //关闭sqlSession
33 //它会根据当前的sqlSession是否在Spring的事物上下文当中来执行具体的关闭动作
34 //如果sqlSession被Spring管理 则调用holder.released(); 使计数器-1
35 //否则才真正的关闭sqlSession
36 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
37 }
38 }
39 }

在上面的invoke方法当中使用了俩个工具方法 分别是

SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)

SqlSessionUtils.closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)

那么这个俩个方法又是如何与Spring的事物进行关联的呢?

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
2 //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式
3 SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
4 //如果holder不为空,且和当前事务同步
5 if (holder != null && holder.isSynchronizedWithTransaction()) {
6 //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用
7 if (holder.getExecutorType() != executorType) {
8 throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
9 }
10 //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加
11 holder.requested();
12 //返回sqlSession
13 return holder.getSqlSession();
14 }
15 //如果找不到,则根据执行类型构造一个新的sqlSession
16 SqlSession session = sessionFactory.openSession(executorType);
17 //判断同步是否激活,只要SpringTX被激活,就是true
18 if (isSynchronizationActive()) {
19 //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务
20 Environment environment = sessionFactory.getConfiguration().getEnvironment();
21 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
22 //如果是,则将sqlSession加载进事务管理的本地线程缓存中
23 holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
24 //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中
25 bindResource(sessionFactory, holder);
26 //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations
27 registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
28 //设置当前holder和当前事务同步
29 holder.setSynchronizedWithTransaction(true);
30 //增加引用数
31 holder.requested();
32 } else {
33 if (getResource(environment.getDataSource()) == null) {
34 } else {
35 throw new TransientDataAccessResourceException(
36 "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
37 }
38 }
39 } else {
40 }
41 return session;
42 }
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
2 //其实下面就是判断session是否被Spring事务管理,如果管理就会得到holder
3 SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
4 if ((holder != null) && (holder.getSqlSession() == session)) {
5 //这里释放的作用,不是关闭,只是减少一下引用数,因为后面可能会被复用
6 holder.released();
7 } else {
8 //如果不是被spring管理,那么就不会被Spring去关闭回收,就需要自己close
9 session.close();
10 }
11 }

其实通过上面的代码我们可以看出 Mybatis在很多地方都用到了代理模式,这个模式可以说是一种经典模式,其实不紧紧在Mybatis当中使用广泛,Spring的事物,AOP ,连接池技术 等技术都使用了代理技术。在后面的文章中我们来分析Spring的抽象事物管理机制。

Mybatis-Spring SqlSessionTemplate 源码解析的更多相关文章

  1. Spring IoC源码解析之getBean

    一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...

  2. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  3. Spring IoC源码解析之invokeBeanFactoryPostProcessors

    一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...

  4. Spring系列(五):Spring AOP源码解析

    一.@EnableAspectJAutoProxy注解 在主配置类中添加@EnableAspectJAutoProxy注解,开启aop支持,那么@EnableAspectJAutoProxy到底做了什 ...

  5. Spring系列(六):Spring事务源码解析

    一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...

  6. Spring系列(三):Spring IoC源码解析

    一.Spring容器类继承图 二.容器前期准备 IoC源码解析入口: /** * @desc: ioc原理解析 启动 * @author: toby * @date: 2019/7/22 22:20 ...

  7. Spring Boot系列(四):Spring Boot源码解析

    一.自动装配原理 之前博文已经讲过,@SpringBootApplication继承了@EnableAutoConfiguration,该注解导入了AutoConfigurationImport Se ...

  8. Spring Security源码解析一:UsernamePasswordAuthenticationFilter之登录流程

    一.前言 spring security安全框架作为spring系列组件中的一个,被广泛的运用在各项目中,那么spring security在程序中的工作流程是个什么样的呢,它是如何进行一系列的鉴权和 ...

  9. Mybatis SqlSessionTemplate 源码解析

    As you may already know, to use MyBatis with Spring you need at least an SqlSessionFactory and at le ...

  10. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

随机推荐

  1. 浅谈 js eval作用域

    原文:浅谈 js eval作用域 就简单聊下如何全局 eval 一个代码. var x = 1; (function () { eval('var x = 123;'); })(); console. ...

  2. ASP.NET4.0新特性

    原文:ASP.NET4.0新特性 在以前试用VS2010的时候已经关注到它在Web开发支持上的一些变化了,为此我还专门做了一个ppt,当初是计划在4月12日那天讲的,结果因为莫名其妙的原因导致没有语音 ...

  3. servlet请求转发与重定向的差别------用生活实例来形象说明两者的差别

    1,请求重定向:client行为,response.sendRedirect(),从本质上讲等同于两次请求,前一次的请求对象不会保留,地址栏的URL地址会改变. 2,请求转发:server行为,req ...

  4. SQL点滴18—SqlServer中的merge操作,相当地风骚

    原文:SQL点滴18-SqlServer中的merge操作,相当地风骚 今天在一个存储过程中看见了merge这个关键字,第一个想法是,这个是配置管理中的概念吗,把相邻两次的更改合并到一起.后来在tec ...

  5. Ubuntu终端字体颜色方案

    默认: 蓝色代表该文件夹: 绿色代表可运行文件. 红色表示压缩文件. 浅蓝色表示链接文件: 灰色表示其它文件: 红色闪烁表示链接的文件有问题了 黄色表示设备文件. 非常不爽怎么办,改! # Attri ...

  6. 4GB内存原32位系统(x86)取舍问题,显卡共享内存Win8.1完全不用担心

    情景:集成显卡 配置: 4G显示3.25GB 此时系统自动将用不到的系统完全共享给显卡(768MB而不是256): 看显卡适配器信息,完全共享给了显卡 解说:上图总可用图形内存 = 图2中备用 + 硬 ...

  7. OpenSUSE 13.2使用VPN(PPTP)

    新年开始,有时查询个资料或是下个软件包并不是那么愉快,决定使用付费VPN,他们使用的是用户名及密码的验证方式 在网上找到了一个教程,挺详尽的,如果想按照步骤能使用即可的原则,跟着我一起设置,想了解更多 ...

  8. 智能的API、云服务和SOA测试解决方案——Parasoft SOAtest

    依赖Parasoft测试解决方案的机构,不仅有小企业,政府机构,还有世界500强集团.Parasoft公司推出的Parasoft SOAtest,提供了API.云服务和SOA最全面的测试解决方案.此次 ...

  9. SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍

    原文:SSMS2008插件开发(2)--Microsoft Visual Studio 2008插件开发介绍 由于开发SSMS2008插件是通过VS2008进行的,有必要先介绍一下VS2008的插件开 ...

  10. S全选功能代码

    JS全选功能代码优化 2014-06-26 00:00 by 龙恩0707, 470 阅读, 3 评论, 收藏, 编辑 JS全选功能代码优化 最近在看javascript MVC那本书,也感觉到自己写 ...