1、先看代码

1.1、spring-config.xml

  1. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  2. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  3. <property name="url" value="jdbc:mysql://localhost:3306/test?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
  4. <property name="username" value="root"/>
  5. <property name="password" value=""/>
  6. </bean>
  7. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  8. <property name="dataSource" ref="dataSource"/>
  9. </bean>

1.2、测试用例

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(locations = "classpath:spring-config.xml")
  3. @TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
  4. )
  5. public class Timeout1Test {
  6. @Autowired
  7. private DataSource ds;
  8. @Test
  9. public void testTimeout() throws InterruptedException {
  10. System.out.println(System.currentTimeMillis());
  11. JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
  12. jdbcTemplate.execute(" update test set name = name || '1'");
  13. System.out.println(System.currentTimeMillis());
  14. Thread.sleep(3000L);
  15. }
  16. }

我设置事务超时时间是2秒;但我事务肯定执行3秒以上;为什么没有起作用呢?  这其实是对Spring实现的事务超时的错误认识。那首先分析下Spring事务超时实现吧。

2、分析

2.1、在此我们分析下DataSourceTransactionManager;首先开启事物会调用其doBegin方法:

  1. …………
  2. int timeout = determineTimeout(definition);
  3. if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
  4. txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
  5. }
  6. …………

其中determineTimeout用来获取我们设置的事务超时时间;然后设置到ConnectionHolder对象上(其是ResourceHolder子类),接着看ResourceHolderSupport的setTimeoutInSeconds实现:

  1. public void setTimeoutInSeconds(int seconds) {
  2. );
  3. }
  4. public void setTimeoutInMillis(long millis) {
  5. this.deadline = new Date(System.currentTimeMillis() + millis);
  6. }

大家可以看到,其会设置一个deadline时间;用来判断事务超时时间的;那什么时候调用呢?首先检查该类中的代码,会发现:

  1. public int getTimeToLiveInSeconds() {
  2. ;
  3. int secs = (int) Math.ceil(diff);
  4. );
  5. return secs;
  6. }
  7. public long getTimeToLiveInMillis() throws TransactionTimedOutException{
  8. if (this.deadline == null) {
  9. throw new IllegalStateException("No timeout specified for this resource holder");
  10. }
  11. long timeToLive = this.deadline.getTime() - System.currentTimeMillis();
  12. );
  13. return timeToLive;
  14. }
  15. private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
  16. if (deadlineReached) {
  17. setRollbackOnly();
  18. throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
  19. }
  20. }

会发现在调用getTimeToLiveInSeconds和getTimeToLiveInMillis,会检查是否超时,如果超时设置事务回滚,并抛出TransactionTimedOutException异常。到此我们只要找到调用它们的位置就好了,那什么地方调用的它们呢? 最简单的办法使用如“IntelliJ IDEA”中的“Find Usages”找到get***的使用地方;会发现:

DataSourceUtils.applyTransactionTimeout会调用DataSourceUtils.applyTimeout,DataSourceUtils.applyTimeout代码如下:

  1. public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {
  2. Assert.notNull(stmt, "No Statement specified");
  3. Assert.notNull(dataSource, "No DataSource specified");
  4. ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
  5. if (holder != null && holder.hasTimeout()) {
  6. // Remaining transaction timeout overrides specified value.
  7. stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
  8. }
  9. ) {
  10. // No current transaction timeout -> apply specified value.
  11. stmt.setQueryTimeout(timeout);
  12. }
  13. }

其中其在stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());中会调用getTimeToLiveInSeconds,此时就会检查事务是否超时;

然后在JdbcTemplate中,执行sql之前,会调用其applyStatementSettings:其会调用DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());设置超时时间;具体可以看其源码;

到此我们知道了在JdbcTemplate拿到Statement之后,执行之前会设置其queryTimeout,具体意思参考Javadoc:

3、结论

写道
Spring事务超时 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行时超时时间(即其queryTimeout)。

4、因此

假设事务超时时间设置为2秒;假设sql执行时间为1秒;

如下调用是事务不超时的

  1. public void testTimeout() throws InterruptedException {
  2. System.out.println(System.currentTimeMillis());
  3. JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
  4. jdbcTemplate.execute(" update test set hobby = hobby || '1'");
  5. System.out.println(System.currentTimeMillis());
  6. Thread.sleep(3000L);
  7. }

而如下事务超时是起作用的;

  1. public void testTimeout() throws InterruptedException {
  2. Thread.sleep(3000L);
  3. System.out.println(System.currentTimeMillis());
  4. JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
  5. jdbcTemplate.execute(" update test set hobby = hobby || '1'");
  6. System.out.println(System.currentTimeMillis());
  7. }

因此,不要忽略应用中如远程调用产生的事务时间和这个事务时间是否对您的事务产生影响。

另外:

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事务超时时间可能存在的错误认识的更多相关文章

  1. Spring事务超时时间可能存在的错误认识

    摘自:http://jinnianshilongnian.iteye.com/blog/1986023, 感谢作者. 1.先看代码 1.1.spring-config.xml <bean id= ...

  2. Spring事务超时、回滚的相关说明

    事务超时: @Transactional(timeout = 60) 如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数 ...

  3. spring cloud 超时时间

    zuul.host.socket-timeout-millis=60000 #zuul socket连接超时zuul.host.connect-timeout-millis=60000 #zull 请 ...

  4. MyBatis6:MyBatis集成Spring事务管理(下篇)

    前言 前一篇文章<MyBatis5:MyBatis集成Spring事务管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基 ...

  5. MyBatis(6):MyBatis集成Spring事务管理(下)

    前一篇文章复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基础上稍微做一点点的进阶:多数据的事务处理.文章内容主要包含两方面: 1.单表多数据的事 ...

  6. Spring事务管理的实现方式:编程式事务与声明式事务

    1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理) Spring实现编程式事务,依赖 ...

  7. Spring事务管理的实现方式之编程式事务与声明式事务详解

    原创说明:本博文为原创作品,绝非他处转载,转载请联系博主 1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码 ...

  8. Spring事务隔离级别与传播机制详解,spring+mybatis+atomikos实现分布式事务管理

    原创说明:本文为本人原创作品,绝非他处转载,转账请注明出处 1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). ...

  9. Spring 使用介绍(七)—— Spring事务

    一.数据库事务概述 1.基本介绍 事务必需满足ACID(原子性.一致性.隔离性和持久性)特性,缺一不可: 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做 ...

随机推荐

  1. centos搭建svn服务器

    1.在centos6.5上面搭建svn服务器,安装svn服务器:yum install subversion 2.在任意目录下创建仓库目录,这里放在/data/mypros目录下 3.执行命令:svn ...

  2. hdu 1848 Fibonacci again and again(简单sg)

    Problem Description 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:F(1)=1;F(2)=2;F(n)=F(n-1)+F(n-2 ...

  3. Openjudge-NOI题库-和为给定数

    题目描述 Description 给出若干个整数,询问其中是否有一对数的和等于给定的数.  输入输出格式 Input/output 输入格式: 共三行: 第一行是整数n(0 < n <= ...

  4. .net mvc 超过了最大请求长度 限制文件上传大小

    在我们的项目中遇到"超过了最大请求长度"如下图所示,是因为IIS默认请求长度4M,当请求长度大于这个值的时候报错,下面是解决方案. 解决方案:修改web.config文件 1.注意 ...

  5. Js的Url中传递中文参数乱码的解决

    一:Js的Url中传递中文参数乱码问题,重点:encodeURI编码,decodeURI解码: 1.传参页面Javascript代码: 2. 接收参数页面:test02.html 二:如何获取Url& ...

  6. HDU 5884 Sort

    二分,验证. 二分$k$,然后进行验证.有一个地方需要注意一下:如果$n$个数,每次合并$k$个,最后一次不能合$k$个,那么一开始需要补$0$之后再合并才是最优的.合并的时候用优先队列合并时间复杂度 ...

  7. 使用 Git 报错 error: src refspec master matches more than one.

    今天在使用 Git push 代码时遇到一个报错: error: src refspec master matches more than one. error: failed to push som ...

  8. python pandas 数据处理

    pandas是基于numpy包扩展而来的,因而numpy的绝大多数方法在pandas中都能适用. pandas中我们要熟悉两个数据结构Series 和DataFrame Series是类似于数组的对象 ...

  9. Hive 常用函数

    参考地址:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF 1. parse_url(url, partToExt ...

  10. Sublime的Package Control的安装

    最近在用Sublime,我想很多人和我一样都是先要安装PackageControl吧! 可是看了网上的好多博客感觉都太繁琐了 对于像我这样的小白来说实在有很多看不懂的地方 相对来说还是官网的那种方法更 ...