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://wely.iteye.com/blog/2311700]

时间: 2024-11-01 01:18:23

Spring事务超时时间可能存在的错误认识的相关文章

spring事务中隔离级别和spring的事务传播机制

Transaction 也就是所谓的事务了,通俗理解就是一件事情.从小,父母就教育我们,做事情要有始有终,不能半途而废. 事务也是这样,不能做一般就不做了,要么做完,要 么就不做.也就是说,事务必须是一个不可分割的整体,就像我们在化学课里学到的原子,原子是构成物质的最小单位.于是,人们就归纳出事务的第一个特性:原子性(Atomicity).我靠,一点都不神秘嘛. 特别是在数据库领域,事务是一个非常重要的概念,除了原子性以外,它还有一个极其重要的特性,那就是:一致性(Consistency).也就

Spring事务——Spring 2.X的事务配置策略

Spring 2.X的事务配置策略     虽然前面介绍的TransactionProxyFactoryBean配置策略简单易懂,但配置起来极为麻烦:每个目标Bean都需要配置一个TransactionProxyFactoryBean代理,这种方式将导致配置文件急剧增加.     Spring 2.X的XMLSchema方式提供了更简洁的事务配置策略,Spring 2.X提供了tx命名空间来配置事务管理,tx命名空间提供了<tx:advice../>元素来配置事务增强处理,一旦使用该元素配置了

shiro和spring集成时session管理器超时时间问题

问题描述 shiro和spring集成时session管理器超时时间问题 这是我的配置文件,我配置了并发人数控制和动态权限过滤,然后session超时时间这里也是配置了的,然后并没有什么鸟用,在登录以后获取超时时间也是正常的,但还是1分钟就过期了. <?xml version="1.0" encoding="UTF-8"?> xmlns:util="http://www.springframework.org/schema/util"

web service-求助:spring webservicetemplate访问webservice怎么设置超时时间?

问题描述 求助:spring webservicetemplate访问webservice怎么设置超时时间? 客户端使用webservicetemplate访问,调用marshalSendAndReceive(String uri, Object requestPayload, WebServiceMessageCallbak requestCallback)方法,怎么样设置超时时间? 解决方案 CXF-WebService设置超时时间 解决方案二: 使用CommonsHttpMessageSe

Spring事务配置的五种方式和spring里面事务的传播属性和事务隔离级别

spring事务配置的五种方式: 前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的. 总结如下: Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSource.TransactionManager这两部

分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager

1 系列目录 分布式事务系列(开篇)提出疑问和研究过程 分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager源码分析 分布式事务系列(1.2)Spring事务体系 分布式事务系列(2.1)分布式事务模型与接口定义 分布式事务系列(3.1)jotm的分布式案例 分布式事务系列(3.2)jotm分布式事务源码分析 分布式事务系列(4.1)Atomikos的分布式案例 2 jdbc事务 2.1 例子 public void save(User user)

Spring事务配置的五种方式 巨全!不看后悔,一看必懂!

前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的.     总结如下:     Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分.     DataSource.TransactionManager这两部分只是会根

Spring事务讲解示例(转)

  Spring 事务Transaction1.事务的属性1.1 事务隔离IsolationLevel1.2 事务传播PropagationBehavior1.3 事务超时Timeout1.4 只读状态ReadOnly隔离级别.超时.只读状态都依赖于底层的数据库实现,Spring仅仅起了代理作用(MySQL的MyISAM引擎是无事务引擎,那么Spring是不可能带有事务管理功能的).事务传播行为是Spring框架所特有的.2 使用Spring事务只需要了解PlatformTransactionM

Spring事务管理只对出现运行期异常进行回滚_java

一.结论 Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚. 如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚. 关于异常的分类一下详细介绍: 1.基本概念 看java的异常结构图  Throwable是所有异常的根,java.lang.Throwable Error是错误,java.lang.Error Exception是异常,java.lang.Exception 2.Excep