中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring事務

發布時間:2020-04-06 06:18:03 來源:網絡 閱讀:292 作者:winters1224 欄目:軟件技術

spring事務基本配置

參見:http://www.cnblogs.com/leiOOlei/p/3725911.html

spring事務傳播機制

參見:http://www.cnblogs.com/aurawing/articles/1887030.html

簡單說一下new和nested的區別。
使用new的時候,外層事務的提交或回滾,與new的事務沒有關系。而使用nested時,內層事務最終是提交還是回滾,需要依賴于外層事務。參見下表。

事務傳播配置 外層事務(set a=1) 內層事務(set b=2) 最終結果
new 提交 提交 a=1 && b=2
new 提交 回滾 a=1
new 回滾 提交 b=2
new 回滾 回滾 什么都不變
nested 提交 提交 a=1 && b=2
nested 提交 回滾 a=1(這種情況需要增加一個配置:<property name="globalRollbackOnParticipationFailure" value="false" />
nested 回滾 提交 什么都不變
nested 回滾 回滾 什么都不變

spring事務隔離機制

參見:Isolation Level(事務隔離等級):

  1. Serializable:最嚴格的級別,事務串行執行,資源消耗最大;
  2. REPEATABLE READ:保證了一個事務不會修改已經由另一個事務讀取但未提交(回滾)的數據。避免了“臟讀取”和“不可重復讀取”的情況,但是帶來了更多的性能損失。
  3. READ COMMITTED:大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的數據,避免了“臟讀取”。該級別適用于大多數系統。
  4. Read Uncommitted:保證了讀取過程中不會讀取到非法數據。隔離級別在于處理多事務的并發問題。

我們知道并行可以提高數據庫的吞吐量和效率,但是并不是所有的并發事務都可以并發運行,這需要查看數據庫教材的可串行化條件判斷了。

這里就不闡述了。
我們首先說并發中可能發生的3中不討人喜歡的事情

  1. Dirty reads--讀臟數據。也就是說,比如事務A的未提交(還依然緩存)的數據被事務B讀走,如果事務A失敗回滾,會導致事務B所讀取的的數據是錯誤的。
  2. non-repeatable reads--數據不可重復讀。比如事務A中兩處讀取數據-total-的值。在第一讀的時候,total是100,然后事務B就把total的數據改成200,事務A再讀一次,結果就發現,total竟然就變成200了,造成事務A數據混亂。
  3. phantom reads--幻象讀數據,這個和non-repeatable reads相似,也是同一個事務中多次讀不一致的問題。但是non-repeatable reads的不一致是因為他所要取的數據集被改變了(比如total的數據),但是phantom reads所要讀的數據的不一致卻不是他所要讀的數據集改變,而是他的條件數據集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候,由于事務b把一個帳號的名字由"dd"改成"ppgogo1",結果取出來了7個數據。
-- Dirty?reads non-repeatable?reads phantom reads
Serializable? 不會 不會 不會
REPEATABLE?READ 不會 不會
READ?COMMITTED 不會
Read?Uncommitted
DEFALT(使用底層數據庫的默認隔離級別)

spring事務管理其他配置

readOnly

標記事務只讀。只讀事務會被優化;但是“只讀”事務中其實可以寫數據。

timeout

事務超時時間

rollbackFor/rollbackForClassName

標記哪些異常會引發回滾。默認情況下,所有RuntimeException都會引發回滾;所有其它異常(checked-exception)都不引發回滾。如果需要針對某種checked-exception進行回滾,則需要為事務配置rollbackFor或者rollbackForClassName

noRollbackFor/noRollbackForClassName

標記哪些異常不會引發回滾。默認情況下,所有RuntimeException都會引發回滾;所有其它異常(checked-exception)都不引發回滾。如果需要使得某種RuntimeException不進行回滾,則需要為事務配置noRollbackFor/noRollbackForClassName。

spring事務機制基本原理

aop

無論配置方式,還是注解方式,spring都是基于spring aop來進行事務管理。
即,在事務切入點處,生成一個動態代理。在代理中管理事務(開啟、傳播、提交或回滾等)。可參見下面兩張圖:
Spring事務

Spring事務

兩張圖都是調用isLocked()方法時的線程棧。可以看到,第二章圖是通過動態代理來調用isLocked()方法的,而第一張圖則不是。
動態代理的方式簡化了代碼的開發;但是也引入了一些小問題。后面會提。

線程上下文

spring會將事務相關的有狀態數據(數據庫連接、hibernate的session等)放在線程上下文中(ThreadLocal)。因此,事務間的傳播關系、事務開啟和關閉的時機,與線程中的方法調用棧很相似。
另外,由于事務與線程相關,因此目前spring的事務管理無法讓多個子線程在同一事務內運行。

如何測試

首先來看看如何確定運行時是否使用了事務、事務是如何傳播的。
首先在log4j中加入以下配置。嚴格說只要記錄了org.springframework.transaction.support.AbstractPlatformTransactionManager的日志即可。另一個logger是為了記錄SQL的,也可以將它替換成其它logger。

<Logger????name="org.springframework.transaction.support.AbstractPlatformTransactionManager"????level="DEBUG"?additivity="false">???
        <AppenderRef?ref="Console"?/>????
        <AppenderRef?ref="AuditAsyncAppender"?/>
</Logger>
<Logger?name="org.hibernate.SQL"?level="TRACE"?additivity="false">
        <AppenderRef?ref="Console"?/>?
       <AppenderRef?ref="AuditAsyncAppender"?/>
</Logger>

然后運行事務相關代碼:

@Testpublic?void?test() {???
        // 這個方法使用默認的傳播方式(REQUIRED)????
        this.lockServiceByDB.tryLock(LockName.EVENT_LOAN,?"23424");????
        // 這個方法使用REQUIRES_NEW的傳播方式????
        this.lockServiceByDB.isLocked(LockName.EVENT_LOAN,?"23424");
}

可以找到如下日志:

2016-07-06 18:01:10,969 DEBUG AbstractPlatformTransactionManager.getTransaction Creating new transaction with name[cn.youcredit.thread.bizaccount.service.impl.LockServiceByDBTestBySpring.test]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-cn.youcredit.thread.common.exception.ServiceException
【中略】
2016-07-06 18:01:11,297 DEBUG AbstractPlatformTransactionManager.handleExistingTransaction Participating in existing transaction
2016-07-06 18:01:11,309 DEBUG SqlStatementLogger.logStatement
select count(1) as col_00 from dblocks dblock0 where dblock0.lockName=? and dblock0.uniKey=?
2016-07-06 18:01:11,325 DEBUG SqlStatementLogger.logStatement
insert into db_locks (lockName, lockTimeAsLong, uniKey) values (?, ?, ?)
【中略】
2016-07-06 18:01:11,331 DEBUG AbstractPlatformTransactionManager.handleExistingTransaction Suspending current transaction, creating new transaction with name [cn.youcredit.thread.bizaccount.service.impl.LockServiceByDB.isLocked]
【中略】
2016-07-06 18:01:11,335 DEBUG SqlStatementLogger.logStatement
select count(1) as col_00 from dblocks dblock0 where dblock0.lockName=? and dblock0.uniKey=?
2016-07-06 18:01:11,337 DEBUG AbstractPlatformTransactionManager.processCommit Initiating transaction commit
2016-07-06 18:01:11,337 DEBUG HibernateTransactionManager.doCommit Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@1a9ea4d9 updates=org.hibernate.engine.spi.ExecutableList@59ca0db6 deletions=org.hibernate.engine.spi.ExecutableList@33293dac orphanRemovals=org.hibernate.engine.spi.ExecutableList@384841e3 collectionCreations=org.hibernate.engine.spi.ExecutableList@571f925f collectionRemovals=org.hibernate.engine.spi.ExecutableList@5eb192b7 collectionUpdates=org.hibernate.engine.spi.ExecutableList@248f1090 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5eb20abb unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
【中略】
2016-07-06 18:01:11,339 DEBUG AbstractPlatformTransactionManager.cleanupAfterCompletion Resuming suspended transaction after completion of inner transaction
2016-07-06 18:01:11,340 DEBUG AbstractPlatformTransactionManager.proce***ollback Initiating transaction rollback
2016-07-06 18:01:11,340 DEBUG HibernateTransactionManager.doRollback Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[cn.youcredit.thread.common.model.auth.UserInfo#system], EntityKey[cn.youcredit.thread.bizaccount.bean.DBLock#139], EntityKey[cn.youcredit.thread.common.model.auth.UserGroupInfo#500]],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@2f90ad92 updates=org.hibernate.engine.spi.ExecutableList@4710332d deletions=org.hibernate.engine.spi.ExecutableList@7930de44 orphanRemovals=org.hibernate.engine.spi.ExecutableList@52891a77 collectionCreations=org.hibernate.engine.spi.ExecutableList@785fd189 collectionRemovals=org.hibernate.engine.spi.ExecutableList@3e900cf4 collectionUpdates=org.hibernate.engine.spi.ExecutableList@412d379c collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5b6dc76c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]

從日志中可以清楚的看到事務操作、傳播的過程:

  1. 首先,運行LockServiceByDBTestBySpring.test()方法時,創建了一個新事務。這是SpringJUnit4Cla***unner的事務邏輯,即每一個單元測試都會啟動一個事務,并且默認情況下,該事務會回滾。
  2. 然后,在執行到this.lockServiceByDB.tryLock()方法時,由于傳播方式是REQUIRED,因此會加入當當前已有的事務中,并執行兩條sql。
  3. 接著執行this.lockServiceByDB.isLocked()方法。由于這個方法的事務傳播方式是REQUIRES_NEW,因此會掛起當前事務,并創建一個新的事務,在新的事務中執行一條SQL。
  4. 新事務執行并提交之后,恢復被掛起的上層事務。并繼續執行。由于后面沒有其它邏輯、代碼,因此開始回滾外層事務。

常見問題

事務標記在protcted或private方法上,導致事務失效

spring的事務注解只對public方法生效,對protcted、friendly(默認)、private方法無效。如果在后面這些方法上標記事務注解,其效果等于沒有標記。

注解繼承不當,導致事務失效

Java注解的基本繼承關系如下。spring的Transactional注解上有Inherited的元注解。

- 未寫@Inherited: 寫了@Inherited的
子類的類上能否繼承到父類的類上的注解?
子類方法,實現了父類上的抽象方法,這個方法能否繼承到注解?
子類方法,繼承了父類上的方法,這個方法能否繼承到注解?
子類方法,覆蓋了父類上的方法,這個方法能否繼承到注解?

例如下面的代碼中,雖然Son在類級別上聲明了事務,但是它的tryLocked()方法并不會啟動新的事務。因為它在父類中沒有聲明事務。

public?class?Father{
    public?void?tryLocked(){}
}

@Transactional(REQUIRES_NEW)
public?class?Son?extends?Father{
}

使用this方式調用,導致事務失效

對于spring aop的動態代理來說,被代理實例和方法是一個“黑盒”。只有在“黑盒”之外才能進行事務管理。而this調用是黑盒內部的調用邏輯,代理無法感知。
因此,像下文這樣的代碼中,isLocked()方法并不會啟動新的事務。

@Transactional(REQUIRES_NEW)
    public?void?isLocked(){
        ……
    }?
    @Transactional()
    public?void?tryLock(){?
        this.isLocked();?
        ……
    }

事務與查詢

雖然查詢操作并不更新數據,但是查詢也需要事務。尤其對hibernate來說。

hibernate操作數據庫時,需要獲取到一個hibernate的session。而這個session也由HibernateTransactionManager來管理。管理的方式與其它有狀態數據一樣,都是放在ThreadLocal中。如果線程上沒有事務管理器,那么就拿不到session。hibernate做查詢時就會報錯:

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134) ~[spring-orm-4.2.2.RELEASE.jar:4.2.2.RELEASE]

有時候明明沒有寫事務注解,也能執行查詢,那么多半是在某個地方默認、或者“偷偷”開了事務。比如繼承SpringTestCase的單元測試會默認開啟事務。或者web環境下,線程池中的線程上遺留了以前綁定的事務管理器。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

离岛区| 新闻| 新郑市| 买车| 中卫市| 桓仁| 黎平县| 醴陵市| 资源县| 贵溪市| 大连市| 双桥区| 满洲里市| 楚雄市| 新巴尔虎右旗| 桐柏县| 平塘县| 锡林郭勒盟| 信宜市| 富民县| 三穗县| 岳普湖县| 大洼县| 张家港市| 贞丰县| 防城港市| 遂溪县| 南宁市| 开平市| 界首市| 淮南市| 沁水县| 新宾| 辰溪县| 章丘市| 贵溪市| 罗城| 漯河市| 锡林浩特市| 芦溪县| 丰宁|