2019独角兽企业重金招聘Python工程师标准>>>

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. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  8. <property name="dataSource" ref="dataSource"/>
  9. </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. public void setTimeoutInMillis(long millis) {
  5. this.deadline = new Date(System.currentTimeMillis() + millis);
  6. }

大家可以看到,其会设置一个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. 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. checkTransactionTimeout(timeToLive <= 0);
  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代码如下:

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/

转载于:https://my.oschina.net/javahongxi/blog/1523722

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

  1. Spring 事务超时时间

    问题:生产环境下插入大量数据,日志报错,数据最终没有入库,显示state报错. 测试和结论:Transactional中设置超时时间5秒,代码中先插入数据,后sleep数秒,没有报错.用另外一个线程更 ...

  2. 妙用Spring的事务超时时间timeout

    1.如何设置Spring事务超时时间 在方法上加注解 @Transactional(rollbackFor= Exception.class,timeout=10) 注:timeout 单位:秒 增加 ...

  3. Spring事务超时探讨

    先抛出一个问题,如下,Spring事务的默认超时时间是多少? 先来看如下代码,方法上加上了事务注解,并设置事务超时时间为2s.两者的区别是一个是在插入之前Sleep了3秒,一个是在插入之后Sleep了 ...

  4. spring事务超时

    转载自http://jinnianshilongnian.iteye.com/blog/1986023 1.先看代码 1.1.spring-config.xml Java代码 <bean id= ...

  5. mysql 事务 超时时间_设置事务超时时间的问题及数据库update和锁

    Oracle的update语句问题: update config t set t.value =1 where t.key='DB_ KEY' 或者: select * from config t w ...

  6. 事务超时时间无效_Java面试题:Spring事务面试考点的集合整理。建议收藏阅读...

    Spring和事务的关系 关系型数据库.某些消息队列等产品或中间件称为事务性资源,因为它们本身支持事务,也能够处理事务. Spring很显然不是事务性资源,但是它可以管理事务性资源,所以Spring和 ...

  7. 查oracle事务超时时间,ORA-24756: 事务处理不存在 分析

    问题描述: 河南在2009年4月28日早09:23出现柜面交易如CDM,FIX,CARDTLR等服务堵塞,且排队不断增加,后通过down掉FIX.CARDTLR服务后使资源得到释放,在事后的分析中,我 ...

  8. spring cloud超时时间设置

    #如果是zuul(网关)的超时时间需要设置zuul.hystrix.ribbon等三部分:#zuul超时设置 #默认1000 zuul.host.socket-timeout-millis=2000 ...

  9. spring接口超时时间配置

    现象:接口默认1min超时,超过时间直接504 考虑到业务,存在超级慢的接口,需要调大超时时间.网上一搜-一言难尽- 大部分文章都提到以下两个配置spring.mvc.async.request-ti ...

最新文章

  1. MATLAB中nargin 的用法
  2. java.lang.String_自己写的java.lang.String可以让jvm加载到吗?
  3. 64位x86-64处理器架构
  4. lame,把ios录音转换为mp3格式
  5. 深度学习笔记(46) 深度卷积网络学习
  6. 使用这些 HTTP 头保护 Web 应用
  7. 擦地机器人毕业设计_救援机器人毕业设计
  8. Python输入和输出
  9. [转载]微软WMV9的标准化进程受阻
  10. icon 的css,【iview】icon样式
  11. html ol标签用罗马数字,HTML重点标签总结
  12. Sqlite数据库锁死问题
  13. Could not initialize proxy - the owning Session was closed
  14. Android开源项目大合集(转载的基础上添加了项目地址)
  15. 使用IDEA连接本地数据库
  16. 2021SC@SDUSC HBase(十三)项目代码分析——WAL写入
  17. rundll32.exe的用途
  18. HTML基础教学课件,HTML基础课件.ppt
  19. 在线新华字典Sidebar Gadget
  20. Python学习,Day2

热门文章

  1. mysql中的sql_mysql中的sql语句
  2. k8s pod部署到不同node_部署Dotnet Core应用到Kubernetes(一) - 老王Plus
  3. java 图像处理 空白_使用Java进行图像处理的一些基础操作
  4. java时间戳动态,是否可以根据窗口元素的时间戳动态生成BigQuery表名?
  5. 爬虫404如何跳过_网站改版之后的一次思考:网站改版会让网站受伤?我们如何让他不降反升...
  6. 两个三维图像互信息python_python – 使用numpy计算成对互信息的最佳方式
  7. FastAPI ------框架基础
  8. java函数实现进制转换与java实现八进制到十进制的转换(百练OJ:2735:八进制到十进制)
  9. 百练OJ:2713:肿瘤面积
  10. 笔记-信息化与系统集成技术-智慧城市建设参考模型