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&useUnicode=true&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];
文章归档
文章标签
友情链接