生活随笔
收集整理的這篇文章主要介紹了
【转】Spring事务超时时间可能存在的错误认识
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1、先看代碼
1.1、spring-config.xml
Java代碼??
<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、測試用例
Java代碼??
@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);??????}??}?? ?
我設(shè)置事務(wù)超時(shí)時(shí)間是2秒;但我事務(wù)肯定執(zhí)行3秒以上;為什么沒有起作用呢???這其實(shí)是對(duì)Spring實(shí)現(xiàn)的事務(wù)超時(shí)的錯(cuò)誤認(rèn)識(shí)。那首先分析下Spring事務(wù)超時(shí)實(shí)現(xiàn)吧。
?
2、分析
2.1、在此我們分析下DataSourceTransactionManager;首先開啟事物會(huì)調(diào)用其doBegin方法:
Java代碼??
…………??int?timeout?=?determineTimeout(definition);??if?(timeout?!=?TransactionDefinition.TIMEOUT_DEFAULT)?{??????txObject.getConnectionHolder().setTimeoutInSeconds(timeout);??}??…………?? ?其中determineTimeout用來獲取我們?cè)O(shè)置的事務(wù)超時(shí)時(shí)間;然后設(shè)置到ConnectionHolder對(duì)象上(其是ResourceHolder子類),接著看ResourceHolderSupport的setTimeoutInSeconds實(shí)現(xiàn):
Java代碼??
public?void?setTimeoutInSeconds(int?seconds)?{??????setTimeoutInMillis(seconds?*?1000);??}????public?void?setTimeoutInMillis(long?millis)?{??????this.deadline?=?new?Date(System.currentTimeMillis()?+?millis);??}?? 大家可以看到,其會(huì)設(shè)置一個(gè)deadline時(shí)間;用來判斷事務(wù)超時(shí)時(shí)間的;那什么時(shí)候調(diào)用呢?首先檢查該類中的代碼,會(huì)發(fā)現(xiàn):
Java代碼??
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);??????}??}?? 會(huì)發(fā)現(xiàn)在調(diào)用getTimeToLiveInSeconds和getTimeToLiveInMillis,會(huì)檢查是否超時(shí),如果超時(shí)設(shè)置事務(wù)回滾,并拋出TransactionTimedOutException異常。到此我們只要找到調(diào)用它們的位置就好了,那什么地方調(diào)用的它們呢??最簡單的辦法使用如“IntelliJ IDEA”中的“Find Usages”找到get***的使用地方;會(huì)發(fā)現(xiàn):
DataSourceUtils.applyTransactionTimeout會(huì)調(diào)用DataSourceUtils.applyTimeout,DataSourceUtils.applyTimeout代碼如下:
Java代碼??
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());中會(huì)調(diào)用getTimeToLiveInSeconds,此時(shí)就會(huì)檢查事務(wù)是否超時(shí);
?
?
然后在JdbcTemplate中,執(zhí)行sql之前,會(huì)調(diào)用其applyStatementSettings:其會(huì)調(diào)用DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());設(shè)置超時(shí)時(shí)間;具體可以看其源碼;
?
到此我們知道了在JdbcTemplate拿到Statement之后,執(zhí)行之前會(huì)設(shè)置其queryTimeout,具體意思參考Javadoc:
?
3、結(jié)論
寫道 Spring事務(wù)超時(shí) = 事務(wù)開始時(shí)到最后一個(gè)Statement創(chuàng)建時(shí)時(shí)間 + 最后一個(gè)Statement的執(zhí)行時(shí)超時(shí)時(shí)間(即其queryTimeout)。
?
4、因此
假設(shè)事務(wù)超時(shí)時(shí)間設(shè)置為2秒;假設(shè)sql執(zhí)行時(shí)間為1秒;
如下調(diào)用是事務(wù)不超時(shí)的
Java代碼??
public?void?testTimeout()?throws?InterruptedException?{??????System.out.println(System.currentTimeMillis());??????JdbcTemplate?jdbcTemplate?=?new?JdbcTemplate(ds);??????jdbcTemplate.execute("?update?test?set?hobby?=?hobby?||?'1'");??????System.out.println(System.currentTimeMillis());??????Thread.sleep(3000L);??}?? 而如下事務(wù)超時(shí)是起作用的;
Java代碼??
public?void?testTimeout()?throws?InterruptedException?{??????Thread.sleep(3000L);??????System.out.println(System.currentTimeMillis());??????JdbcTemplate?jdbcTemplate?=?new?JdbcTemplate(ds);??????jdbcTemplate.execute("?update?test?set?hobby?=?hobby?||?'1'");??????System.out.println(System.currentTimeMillis());??}?? ??
?
因此,不要忽略應(yīng)用中如遠(yuǎn)程調(diào)用產(chǎn)生的事務(wù)時(shí)間和這個(gè)事務(wù)時(shí)間是否對(duì)您的事務(wù)產(chǎn)生影響。
?
另外:
1、事務(wù)超時(shí)不起作用,您要首先檢查您的事務(wù)起作用了沒:可以參考使用Aop工具類診斷常見問題
2、如果您用的JPA,且spring版本低于3.0,可能您的事務(wù)超時(shí)不起作用:https://jira.springsource.org/browse/SPR-5195
3、如果您用JDBC,但沒有用JdbcTemplate,直接使用DateSourceUtils進(jìn)行事務(wù)控制時(shí),要么自己設(shè)置Statement的queryTimeout超時(shí)時(shí)間,要么使用TransactionAwareDataSourceProxy,其在創(chuàng)建Statement時(shí)會(huì)自動(dòng)設(shè)置其queryTimeout。
4、關(guān)于JDBC超時(shí)時(shí)間設(shè)置一篇不錯(cuò)的翻譯:深入理解JDBC的超時(shí)設(shè)置?
http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/
轉(zhuǎn)載于:https://my.oschina.net/javahongxi/blog/1523722
總結(jié)
以上是生活随笔為你收集整理的【转】Spring事务超时时间可能存在的错误认识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。