MyBatist庖丁解牛(五)
很多时候我们在自己的每个service中没有中注入SqlSessionTemplate;
但是我们直接调用mapper接口方法就直接能够操作数据库 这个是为什么??下面开始解惑:
Mybatis SqlSessionTemplate 源码解析
在使用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, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
核心代码就在 SqlSessionInterceptor的invoke方法当中。
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
} unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
} throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
} } return unwrapped;
}
}
在上面的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) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
LOGGER.debug(() -> {
return "Creating a new SqlSession";
});
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
SqlSession session = null;
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
} holder.requested();
LOGGER.debug(() -> {
return "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction";
});
session = holder.getSqlSession();
} return session;
}
1 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 }
1 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的抽象事物管理机制。
MyBatist庖丁解牛(五)的更多相关文章
- MyBatist庖丁解牛(一)
站在巨人的肩膀上,感谢! https://www.jianshu.com/p/ec40a82cae28?utm_campaign=maleskine&utm_content=note& ...
- MyBatist庖丁解牛(四)
什么是MyBatis-Spring? MyBatis-Spring就是帮助你将MyBatis代码无缝的整合到Spring中.Spring将会加载必要的sqlSessionFactory类和sessio ...
- MyBatist庖丁解牛(三)
从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个: SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能: Ex ...
- MyBatist庖丁解牛(二)
站在巨人的肩膀上 https://blog.csdn.net/xiaokang123456kao/article/details/76228684 一.概述 我们知道,Mybatis实现增删改查需要进 ...
- [转载]Ubuntu 安装 万能五笔 输入法
原文地址:Ubuntu 安装 万能五笔 输入法作者:庖丁解牛 paul@paul-desktop:~/scripts$ cat ins-ibus-wnwb.sh #!/bin/sh set -e cd ...
- 2019-2020-1 20199326《Linux内核原理与分析》第五周作业
第五周学习内容 庖丁解牛Linux内核分析第四章:系统调用的三层机制(上) Linux内核分析实验四 学到的一些知识 4.1用户态.内核态.中断 宏观上Linux操作系统的体系架构分为用户态和内核态 ...
- 干货 | 45张图庖丁解牛18种Queue,你知道几种?
在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...
- 《Django By Example》第五章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者@ucag注:大家好,我是新来的翻译, ...
- 旺财速啃H5框架之Bootstrap(五)
在上一篇<<旺财速啃H5框架之Bootstrap(四)>>做了基本的框架,<<旺财速啃H5框架之Bootstrap(二)>>篇里也大体认识了bootst ...
随机推荐
- java的一个简单死锁的例子
package com.deadlock; /* * 演示死锁:(由毕向东视频所得) * 一种解释:Thread—0拿到lock1锁,Thread—1拿到lock2锁,Thread—0想要lock2锁 ...
- MySql in子句 效率低下优化(亲测有效,从200秒变1秒)
MySql in子句 效率低下优化 背景: 更新一张表中的某些记录值,更新条件来自另一张含有200多万记录的表,效率极其低下,耗时高达几分钟. update clear_res set candele ...
- 如何分析一个已有的Delphi项目源代码
分析一个已有的Delphi项目,应该从以下入手(按先后顺序):1. 编译条件,包括自定义的Condition以及inc文件里的标识2. 主项目文件dpr,因为窗体的windows消息循环只是程序的一部 ...
- 最新版本号MYSQL官网下载地址可是必需要注冊后才干下载
因mysql5.0上运行函数不行,决定安装最新版本号的mysql,在网上找了些绿色版的.安装总报1067错误,网上的各种方法都试了,就是不行.浪费时间不说.郁闷死了,最后决定去官网,官 ...
- ubuntu 12.04 解压安装jdk
ubuntu下解压安装jdk,简单方便.分享一下安装方法: 注:该方法针对新系统,之前没有配置过jdk的情况. 1.下载相应版本号的jdk压缩包.如 jdk-8u5-linux-x64.gz 2.解压 ...
- php mcrypt加密实例
<?php //当前mcrypt支持的加密模型 $modes_list = mcrypt_list_modes(); // Array // ( // [0] => cbc // [1] ...
- Gym - 100283F F. Bakkar In The Army —— 二分
题目链接:http://codeforces.com/gym/100283/problem/F F. Bakkar In The Army time limit per test 2 seconds ...
- discuz邮箱注册激活||腾讯企业邮箱免费注册及登录方法
如何申请免费的企业邮箱,如果拥有了网站,还能有一个免费的域名邮箱,是不是很拉风呢?对于还没有注册企业的用户来说,优先使用企业邮箱,是非常好的事呢. 腾讯邮箱现在开放免费的企业邮箱注册,效果要比个人邮箱 ...
- WPF Combo box 获取选择的Tag
string str1 = ((ComboBoxItem)this.cboBoxRate1553B.Items[this.cboBoxRate1553B.SelectedIndex]).Tag.ToS ...
- v-for指令用法二
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...