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. PHP基础语法思维导图

  2. VMware虚拟机服务的vmware-hostd自动启动和停止

    安装了虚拟机 任务管理器会出现vmware-hostd.exe  占用了80端口,导致xampp打不开,所以就想关闭vmware,解决方案如下: 开始——运行——services.msc,找到VM打头 ...

  3. 图片上传插件用法,net语法【二】

    之前一直写过KindeEditor中的小控件作为单独上次,但业务要求需要另一种方式 现在改用ajaxfileupload.js试试,这个一百度 一.首页引用 <script src=" ...

  4. jsp/servlet中的编码问题

    首先声明以下只是我个人的看法,有部分观点与网上人云亦云的观点不一样,不过凡事必恭亲,我还是相信自己测试的结果 推荐一个很好地URL编码详解http://www.ruanyifeng.com/blog/ ...

  5. C#第十三天

    1.单例模式 1)将构造函数私有化 2)提供一个静态方法,返回一个对象 3)创建一个单例 namespace 单例模式 { public partial class Form1 : Form { pu ...

  6. Ubuntu 忘记密码

    1重启电脑Shift键进入GRUB引导模式如下图所示,选择第二行的recovery mode. 2 安e进入recovery mode 编译kernel进行启动参数 3 在linux /boot/vm ...

  7. 【Python之路】第二篇--初识Python

    Python简介 Python可以应用于众多领域,如:数据分析.组件集成.网络服务.图像处理.数值计算和科学计算等众多领域.目前业内几乎所有大中型互联网企业都在使用Python,如:Youtube.D ...

  8. hibernate增删改查

    -----------增加--------- public void insertUsers(String userName,String userPwd) { Users u=new Users() ...

  9. 记录下actionbar的翻译

    http://blog.csdn.net/xyz_lmn/article/details/8132420 嗯,actionbarSherLock不错,viewpagerIndicator也不错.

  10. vue router 只需要这么几步

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...