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

编程技术  /  houtizong 发布于 3年前   59

1、先看代码

1.1、spring-config.xml

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

 

1.2、测试用例

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:spring-config.xml")@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)@Transactional(timeout = 2)public class Timeout1Test {    @Autowired    private DataSource ds;    @Test    public void testTimeout() throws InterruptedException {        System.out.println(System.currentTimeMillis());        JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);        jdbcTemplate.execute(" update test set name = name || '1'");        System.out.println(System.currentTimeMillis());        Thread.sleep(3000L);    }}

 

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

 

2、分析

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

…………int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}…………

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

public void setTimeoutInSeconds(int seconds) {setTimeoutInMillis(seconds * 1000);}public void setTimeoutInMillis(long millis) {this.deadline = new Date(System.currentTimeMillis() + millis);}

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

public int getTimeToLiveInSeconds() {double diff = ((double) getTimeToLiveInMillis()) / 1000;int secs = (int) Math.ceil(diff);checkTransactionTimeout(secs <= 0);return secs;}public long getTimeToLiveInMillis() throws TransactionTimedOutException{if (this.deadline == null) {throw new IllegalStateException("No timeout specified for this resource holder");}long timeToLive = this.deadline.getTime() - System.currentTimeMillis();checkTransactionTimeout(timeToLive <= 0);return timeToLive;}private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {if (deadlineReached) {setRollbackOnly();throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);}}

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

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

public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {Assert.notNull(stmt, "No Statement specified");Assert.notNull(dataSource, "No DataSource specified");ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (holder != null && holder.hasTimeout()) {// Remaining transaction timeout overrides specified value.stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());}else if (timeout > 0) {// No current transaction timeout -> apply specified value.stmt.setQueryTimeout(timeout);}}

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

 

 

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

 

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

 

3、结论

写道

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客