`
IXHONG
  • 浏览: 440593 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【转】Spring事务超时时间可能存在的错误认识

阅读更多

1、先看代码

1.1、spring-config.xml

Java代码  收藏代码
  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.   
  8. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  9.     <property name="dataSource" ref="dataSource"/>  
  10. </bean>  

 

1.2、测试用例

Java代码  收藏代码
  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration(locations = "classpath:spring-config.xml")  
  3. @TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)  
  4. @Transactional(timeout = 2)  
  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方法:

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

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

Java代码  收藏代码
  1. public void setTimeoutInSeconds(int seconds) {  
  2.     setTimeoutInMillis(seconds * 1000);  
  3. }  
  4.   
  5. public void setTimeoutInMillis(long millis) {  
  6.     this.deadline = new Date(System.currentTimeMillis() + millis);  
  7. }  

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

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

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

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

Java代码  收藏代码
  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.     else if (timeout > 0) {  
  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秒;

如下调用是事务不超时的

Java代码  收藏代码
  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. }  

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

Java代码  收藏代码
  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事务与数据库操作

    spring事务与数据库操作

    Spring事务原理、Spring事务配置的五种方式

    Spring事务原理和配置 Spring事务原理是指Spring框架中的一种机制,用于管理事务,并提供了多种配置方式。事务是指一系列的操作,作为一个整体执行,如果其中某个操作失败,整个事务将回滚。Spring事务原理围绕着两...

    Spring事务管理Demo

    Spring事务管理Demo

    Spring事务流程图

    Spring事务流程图时序图Spring事务流程图时序图Spring事务流程图时序图Spring事务流程图时序图

    Spring事务测试题及原理

    此ppt中前半部分通过spring事务的60道题的测试,摸底对事务的掌握情况,后半部分,对spring中的事务属性(传播行为、隔离级别、回滚规则、事务超时、是否只读)进行说明

    spring事务配置详解

    spring事务配置详解 spring事务配置详解

    spring 事务(6中配置完全降解)

    spring 事务spring 事务spring 事务spring 事务spring 事务

    spring学习事务源码

    spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码

    Spring中事务的传播属性详解

    Spring中事务的传播属性详解,Spring中事务的传播属性详解

    spring 事务传播 demo

    spring 事务传播 demo

    spring事务的底层实现流程图

    spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务...

    spring事务源码解析

    spring事务源码解析

    Spring事务操作示例(四种方式)

    Spring事务操作示例(四种方式),包含完整代码和数据库文件(基于MySQL,在项目sql文件夹中),可运行,学习Spring事务详见博客:http://blog.csdn.net/daijin888888/article/details/51822257

    实验 spring 声明事务

    Java高级编程 实验报告 spring 声明事务 实验目的 掌握spring 声明式事务管理配置 实验环境 本实验采用本实验采用的eclipse或者 Myeclpse开发工具。Spring 4.0以上 Jdk1.7以上、oracle/mysql。

    spring事务详解

    Spring的事务框架将开发过程中事务管理相关的关注点进行适当的分离,并对这些关注点进行合 理的抽象,最终打造了一套使用方便,却功能强大的事务管理“利器”。通过Spring的事务框架,我 们可以按照统一的编程模型来...

    Spring事务传播特性解析

    通过代码解析spring传播特性,包括 ... 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。需要JDBC3.0以上支持。

    spring事务控制

    spring事务控制jar包,请网上自寻下载

    Spring事务管理.pdf

    Spring事务管理.pdf 1.资料 2.本地事务与分布式事务 3.编程式模型 4.宣告式模型

    Spring事务失效Spring事务失效

    Spring事务失效Spring事务失效

    spring 事务基于注解模式

    spring 事务基于注解模式

Global site tag (gtag.js) - Google Analytics