【转】Spring事务超时时间可能存在的错误认识
1、先看代码
1.1、spring-config.xml
- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf-8"/>
- <property name="username" value="root"/>
- <property name="password" value=""/>
- </bean>
- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"/>
- </bean>
1.2、测试用例
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = "classpath:spring-config.xml")
- @TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
- )
- public class Timeout1Test {
- @Autowired
- private DataSource ds;
- @Test
- public void testTimeout() throws InterruptedException {
- System.out.println(System.currentTimeMillis());
- JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
- jdbcTemplate.execute(" update test set name = name || '1'");
- System.out.println(System.currentTimeMillis());
- Thread.sleep(3000L);
- }
- }
我设置事务超时时间是2秒;但我事务肯定执行3秒以上;为什么没有起作用呢? 这其实是对Spring实现的事务超时的错误认识。那首先分析下Spring事务超时实现吧。
2、分析
2.1、在此我们分析下DataSourceTransactionManager;首先开启事物会调用其doBegin方法:
- …………
- int timeout = determineTimeout(definition);
- if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
- txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
- }
- …………
其中determineTimeout用来获取我们设置的事务超时时间;然后设置到ConnectionHolder对象上(其是ResourceHolder子类),接着看ResourceHolderSupport的setTimeoutInSeconds实现:
- public void setTimeoutInSeconds(int seconds) {
- );
- }
- public void setTimeoutInMillis(long millis) {
- this.deadline = new Date(System.currentTimeMillis() + millis);
- }
大家可以看到,其会设置一个deadline时间;用来判断事务超时时间的;那什么时候调用呢?首先检查该类中的代码,会发现:
- public int getTimeToLiveInSeconds() {
- ;
- int secs = (int) Math.ceil(diff);
- );
- return secs;
- }
- public long getTimeToLiveInMillis() throws TransactionTimedOutException{
- if (this.deadline == null) {
- throw new IllegalStateException("No timeout specified for this resource holder");
- }
- long timeToLive = this.deadline.getTime() - System.currentTimeMillis();
- );
- return timeToLive;
- }
- private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
- if (deadlineReached) {
- setRollbackOnly();
- throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
- }
- }
会发现在调用getTimeToLiveInSeconds和getTimeToLiveInMillis,会检查是否超时,如果超时设置事务回滚,并抛出TransactionTimedOutException异常。到此我们只要找到调用它们的位置就好了,那什么地方调用的它们呢? 最简单的办法使用如“IntelliJ IDEA”中的“Find Usages”找到get***的使用地方;会发现:
DataSourceUtils.applyTransactionTimeout会调用DataSourceUtils.applyTimeout,DataSourceUtils.applyTimeout代码如下:
- public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {
- Assert.notNull(stmt, "No Statement specified");
- Assert.notNull(dataSource, "No DataSource specified");
- ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
- if (holder != null && holder.hasTimeout()) {
- // Remaining transaction timeout overrides specified value.
- stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
- }
- ) {
- // No current transaction timeout -> apply specified value.
- stmt.setQueryTimeout(timeout);
- }
- }
其中其在stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());中会调用getTimeToLiveInSeconds,此时就会检查事务是否超时;
然后在JdbcTemplate中,执行sql之前,会调用其applyStatementSettings:其会调用DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());设置超时时间;具体可以看其源码;
到此我们知道了在JdbcTemplate拿到Statement之后,执行之前会设置其queryTimeout,具体意思参考Javadoc:
3、结论
4、因此
假设事务超时时间设置为2秒;假设sql执行时间为1秒;
如下调用是事务不超时的
- public void testTimeout() throws InterruptedException {
- System.out.println(System.currentTimeMillis());
- JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
- jdbcTemplate.execute(" update test set hobby = hobby || '1'");
- System.out.println(System.currentTimeMillis());
- Thread.sleep(3000L);
- }
而如下事务超时是起作用的;
- public void testTimeout() throws InterruptedException {
- Thread.sleep(3000L);
- System.out.println(System.currentTimeMillis());
- JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
- jdbcTemplate.execute(" update test set hobby = hobby || '1'");
- System.out.println(System.currentTimeMillis());
- }
因此,不要忽略应用中如远程调用产生的事务时间和这个事务时间是否对您的事务产生影响。
另外:
1、事务超时不起作用,您要首先检查您的事务起作用了没:可以参考使用Aop工具类诊断常见问题
2、如果您用的JPA,且spring版本低于3.0,可能您的事务超时不起作用:https://jira.springsource.org/browse/SPR-5195
3、如果您用JDBC,但没有用JdbcTemplate,直接使用DateSourceUtils进行事务控制时,要么自己设置Statement的queryTimeout超时时间,要么使用TransactionAwareDataSourceProxy,其在创建Statement时会自动设置其queryTimeout。
4、关于JDBC超时时间设置一篇不错的翻译:深入理解JDBC的超时设置
http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/
【转】Spring事务超时时间可能存在的错误认识的更多相关文章
- Spring事务超时时间可能存在的错误认识
摘自:http://jinnianshilongnian.iteye.com/blog/1986023, 感谢作者. 1.先看代码 1.1.spring-config.xml <bean id= ...
- Spring事务超时、回滚的相关说明
事务超时: @Transactional(timeout = 60) 如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数 ...
- spring cloud 超时时间
zuul.host.socket-timeout-millis=60000 #zuul socket连接超时zuul.host.connect-timeout-millis=60000 #zull 请 ...
- MyBatis6:MyBatis集成Spring事务管理(下篇)
前言 前一篇文章<MyBatis5:MyBatis集成Spring事务管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基 ...
- MyBatis(6):MyBatis集成Spring事务管理(下)
前一篇文章复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基础上稍微做一点点的进阶:多数据的事务处理.文章内容主要包含两方面: 1.单表多数据的事 ...
- Spring事务管理的实现方式:编程式事务与声明式事务
1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理) Spring实现编程式事务,依赖 ...
- Spring事务管理的实现方式之编程式事务与声明式事务详解
原创说明:本博文为原创作品,绝非他处转载,转载请联系博主 1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码 ...
- Spring事务隔离级别与传播机制详解,spring+mybatis+atomikos实现分布式事务管理
原创说明:本文为本人原创作品,绝非他处转载,转账请注明出处 1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). ...
- Spring 使用介绍(七)—— Spring事务
一.数据库事务概述 1.基本介绍 事务必需满足ACID(原子性.一致性.隔离性和持久性)特性,缺一不可: 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做 ...
随机推荐
- 最强DE 战斗力 (nyoj 541)
题解链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=541 几天前百度题解后用数学知识AC的,后来大牛说这是一道动态规划题. 网上的数学解题链接 ...
- [转]numpy性能优化
转自:http://blog.csdn.net/pipisorry/article/details/39087583 http://blog.csdn.net/pipisorry/article/de ...
- js函数的可变参数
//对于js的可变参数的清空,在定义函数式不需要写上参数, 在函数内部使用argument对象可以 直接获取参数个数等信息 //在调用函数式可以传递任意个数的参数 function text(){ v ...
- 【转】判断URL是否能够访问
import urllib2 def file_exists(url): request = urllib2.Request(url) request.get_method = lambda : 'H ...
- 编写CodeMirror Modes详解
在写新的模式(mode)之前,还是先来概括一下CodeMirror的设计思路.CodeMirror分为三部分:核心,模式,扩展.核心库对外开放了很多接口,而这些接口就是实现新模式或者新扩展的基石. 在 ...
- PyCharm基本使用
调节PyCharm的背景颜色 File>Settings>Appearance&Behavior>Appearance 在PyCharm中切换Python解释器版本 File ...
- Gulp安装使用教程
题记:为什么要使用gulp,网上有很多关于gulp的优势,而在我看来,这些都是工具的优势!工具的优势最主要体现在易用性上,听说gulp比grunt更易用,所以这里写个文档记录. 同样要保证nodejs ...
- 关于手动关闭BootStrap模态框
在网上找手动关闭BootStrap模态框的解决方法,说是(需要引用bootstrap.js等): $("#myModal").modal('hide'); 但是我发现我的只能关闭 ...
- 仿简书分享:UIActivityViewController系统原生分享
接下来介绍UIActivityViewController: 1. 创建要分享的数据内容,加在一个数组 ActivityItems里. NSString *textToShare = @"我 ...
- sql中判断是否存在某个对象
If object_id(N'对象名',N'对象类型') is not null 例如:表是否存在 if object_id(N'tablename',N'U') is not null begi ...