关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.
主要以结果为导向解释Spring 事务原理,连接池的消耗,以及事务内开启事务线程要注意的问题.
Spring 事务原理这里不多说,网上一搜一大堆,也就是基于AOP配合ThreadLocal实现.
这里强调一下Spring Aop 以及Spring 注解式注入在非Spring容器管理的类中是无效的.
因为Spring Aop是在运行时实现字节码增强,字节码增强有多种实现方法,请自行了解,原生AspectJ是编译时织入,但是需要特定的编译器.语法并没有Spring Aop好理解.
先看下Spring的 事务传播行为类型
|
事务传播行为类型 |
说明 |
|
PROPAGATION_REQUIRED |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择。 |
|
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
|
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常。 |
|
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
|
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
|
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
|
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED类似的操作。 |
打开日记debug模式,留意控制台输出
以下测试为了可读性以及更容易理解全是基于Spring注解式事务,而没有配置声明式事务.
测试1:
可以看见只创建了一个SqlSession以及一个事务,在方法内所有操作都使用同一个连接,同一个事务
@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)
@Transactional(propagation = Propagation.REQUIRED)
public void testThreadTx(){
//此方法没有事务(当前方法是 Propagation.REQUIRED)
Quotation quotation = quotationService.findEntityById(new String("1"));
//此方法没有事务(当前方法是 Propagation.REQUIRED)
quotationService.updateEntity(quotation);
}
//查看控制台输出(红字关键部分,第三个查询是更新方法内部需要先查询一次再更新,可以无视)
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
ansaction(line/:54) -JDBC Connection [1068277098(com.mysql.jdbc.JDBC4Connection@5d92bace)] will be managed by Spring
otationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1
otationMapper.findEntityById(line/:54) -==> Parameters:
otationMapper.findEntityById(line/:54) -<== Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4] from current transaction
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4] from current transaction
otationMapper.updateEntity(line/:54) -==> Preparing: update ….. where id = 1
otationMapper.updateEntity(line/:54) -==> Parameters:
otationMapper.updateEntity(line/:54) -<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
.impl.UserOperationLogServiceImpl(line/:41) -请求所用时间:207
.impl.UserOperationLogServiceImpl(line/:42) -请求结束*******************************************************************************
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@230376b4]
测试2:不使用事务
可以看出在非事务操作数据库,会使用多个连接,非常不环保,这里给稍微多线程插入埋下一个陷阱
@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)
// @Transactional(propagation = Propagation.REQUIRED)
public void testThreadTx(){
Quotation quotation = quotationService.findEntityById(new String("1"));
quotationService.updateEntity(quotation);
}
//查看控制台输出(红字关键部分,第三个查询是更新方法内部需要先查询一次再更新,可以无视)
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f7b94f] was not registered for synchronization because synchronization
ansaction(line/:54) -JDBC Connection [352410768(com.mysql.jdbc.JDBC4Connection@c63fcb6)] will not be managed by Spring
otationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1
otationMapper.findEntityById(line/:54) -==> Parameters:
otationMapper.findEntityById(line/:54) -<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f7b94f]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a41785a] was not registered for synchronization because synchronization
ansaction(line/:54) -JDBC Connection [1615108970(com.mysql.jdbc.JDBC4Connection@38377d86)] will not be managed by Spring
otationMapper.findEntityById(line/:54) -==> Preparing: SELECT * FROM table WHERE id = 1
otationMapper.findEntityById(line/:54) -==> Parameters:
otationMapper.findEntityById(line/:54) -<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a41785a]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181e5a22] was not registered for synchronization because synchronization
ansaction(line/:54) -JDBC Connection [2096339748(com.mysql.jdbc.JDBC4Connection@5d4e9892)] will not be managed by Spring
otationMapper.updateEntity(line/:54) -==> Preparing: update …. where id = 1
otationMapper.updateEntity(line/:54) -==> Parameters:
otationMapper.updateEntity(line/:54) -<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@181e5a22]
.impl.UserOperationLogServiceImpl(line/:41) -请求所用时间:614
.impl.UserOperationLogServiceImpl(line/:42) -请求结束*******************************************************************************
测试3:
@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)
@Transactional(propagation = Propagation.REQUIRED)
public void testThreadTx(){
final ExecutorService executorService = Executors.newFixedThreadPool(3);
Quotation quotation = quotationService.findEntityById(new String("1"));
quotationService.updateEntity(quotation);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>(3);
for(int i=0;i<3;i++){
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Quotation quotation = quotationService.findEntityById(new String("1"));
quotationService.updateEntity(quotation);
return null;
}
};
futures.add(executorService.submit(task));
}
executorService.shutdown();
}
//查看控制台输出(红字关键部分,第三个查询是更新方法内部需要先查询一次再更新,可以无视)
为了节篇幅,这里不贴出控制台数据
大概就是输出了10个Creating a new SqlSession(大概有些同学使用了多线程,把线程池耗完了也没弄明白原因)
外层方法启动一个,内部3个线程,每个线程3个.一共是使用了10个连接.
为什么?这涉及到ThreadLocal以及线程私有栈的概念.如果Spring 事务使用InhertableThreadLocal就可以把连接传到子线程,但是为什么Spring不那么干呢?因为这样毫无意义,如果把同一个连接传到子线程,那就是SQL操作会串行执行,那何必还多线程呢?
有关于ThreadLocal,InhertableThreadLocal配合线程池的一些陷阱
请看我另一篇文章:
ThreadLoacl,InheritableThreadLocal,原理,以及配合线程池使用的一些坑
测试4:
既然使用同一个事务,不能实现并发操作,那么只能折中,在每一个线程开启一个事务,减少创建更多的连接,执行完毕以后可以返回操作成功失败结果,反馈给用户
@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)
// @Transactional(propagation = Propagation.REQUIRED)
public void testThreadTx(){
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>(3);
for(int i=0;i<3;i++){
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
quotationService.doSomeThing();
return null;
}
};
futures.add(executorService.submit(task));
}
executorService.shutdown();
} //封装一下
@Override
@Transactional(propagation =Propagation.REQUIRED)
public void doSomeThing(){
Quotation quotation = this.findEntityById(new String("1"));
this.updateEntity(quotation);
}
//查看控制台输出,只会创建3个连接,为节省篇幅,这里不贴出控制台所有数据
Creating a new SqlSession
Creating a new SqlSession
Creating a new SqlSession
最后小技巧PROPAGATION_NOT_SUPPORTED(仅仅为了让Spring能获取ThreadLocal的connection),如果不使用事务,但是同一个方法多个对数据库操作,那么使用这个传播行为可以减少消耗数据库连接
@RequestMapping(value="/testThreadTx",method = RequestMethod.GET)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testThreadTx(){
Quotation quotation = quotationService.findEntityById(new String("1"));
quotation.setStatus(ClassDataManager.STATE_N);
quotationService.updateEntity(quotation);
}
//这样只会创建一个SqlSession
关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.的更多相关文章
- spring 5.x 系列第6篇 —— 整合 mybatis + druid 连接池 (代码配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- spring 5.x 系列第5篇 —— 整合 mybatis + druid 连接池 (xml配置方式)
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 项目目录结构 1.创建maven工程,除了Spring基本依赖外,还需要导 ...
- Spring Boot (三): ORM 框架 JPA 与连接池 Hikari
前面两篇文章我们介绍了如何快速创建一个 Spring Boot 工程<Spring Boot(一):快速开始>和在 Spring Boot 中如何使用模版引擎 Thymeleaf 渲染一个 ...
- Mysql 事务及其原理
Mysql 事务及其原理 什么是事务 什么是事务?事务是作为单个逻辑工作单元执行的一系列操作,通俗易懂的说就是一组原子性的 SQL 查询.Mysql 中事务的支持在存储引擎层,MyISAM 存储引擎不 ...
- 个人MySQL的事务特性原理学习笔记总结
目录 个人MySQL的事务特性原理笔记总结 一.基础概念 2. 事务控制语句 3. 事务特性 二.原子性 1. 原子性定义 2. 实现 三.持久性 1. 定义 2. 实现 3. redo log存在的 ...
- 深入理解Spring Boot数据源与连接池原理
Create by yster@foxmail.com 2018-8-2 一:开始 在使用Spring Boot数据源之前,我们一般会导入相关依赖.其中数据源核心依赖就是spring‐boot‐s ...
- 连接SQLServer时,因启用连接池导致孤立事务的原因分析和解决办法
本文出处:http://www.cnblogs.com/wy123/p/6110349.html 之前遇到过这么一种情况: 连接数据库的部分Session会出现不定时的阻塞,这种阻塞时长时短,有时候持 ...
- C3P0连接池在hibernate和spring中的配置
首先为什么要使用连接池及为什么要选择C3P0连接池,这里就不多说了,目前C3P0连接池还是比较方便.比较稳定的连接池,能与spring.hibernate等开源框架进行整合. 一.hibernate中 ...
- Spring中常用的连接池配置
首先,我们准备Jdbc属性文件 jdbc.properties,用于保存连接数据库的信息,利于我们在配置文件中的使用 jdbc.driver=com.mysql.jdbc.Driver jdbc.ur ...
随机推荐
- HotSpot 虚拟机的算法实现
- Linux指令--cd,pwd
本文参照博客做了修改和总结,感谢作者的分享.http://www.cnblogs.com/peida/archive/2012/10/24/2736501.html cd 命令可以说是Linux中最基 ...
- org.apache.catalina.util.DefaultAnnotationProcessor cannot be cast to org.apache.AnnotationProcessor
这几天来公司,公司的SVN坏掉了,然后项目还比较大,是一个分布式的,然后同事就把项目发我了.我在myeclipse里面导入项目了,把相应的jar包也建了个人的library导入了项目,现在项目不报错了 ...
- linkin大话设计模式--代理模式
代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象的时候,客户端并不关心是否可以准确的得到这个对象,他只要一个能够提供该功能的对象而已,此时我们就可以返回该对象的代理.总而言之,客户端 ...
- 【Java入门提高篇】Day13 Java中的反射机制
前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...
- Installing VirtualBox
The (VirtualBox) website has a lot of quality documentation including: End-user documentation Techni ...
- BZOJ 1299: [LLH邀请赛]巧克力棒 [组合游戏]
每次一人可以从盒子里取出若干条巧克力棒,或是将一根取出的巧克力棒吃掉正整数长度. Nim游戏多了一个决策:拿出一些石堆 显然只要给对方构造异或和为0的子集就行了 暴枚子集... #include &l ...
- 帝国CMS备份出现数据恢复不完整的问题
今天linux主机中毒了,把用帝国备份王备份之后,恢复了快照到刚建主机的状态: 哎,只怪当初没有勤快的去做快照啊: 重新配置好后: 开始使用帝国备份王: 数据恢复之后,打开文章,提示"附加表 ...
- 撸一撸Spring Cloud Ribbon的原理
说起负载均衡一般都会想到服务端的负载均衡,常用产品包括LBS硬件或云服务.Nginx等,都是耳熟能详的产品. 而Spring Cloud提供了让服务调用端具备负载均衡能力的Ribbon,通过和Eure ...
- u-boot核心初始化
异常向量表:异常:因为内部或者外部的一些事件,导致处理器停下正在处理的工作,转而去处理这些发生的事件.ARM Architecture Reference Manual p54页.7种异常的类型:Re ...