首先建立一個Maven project,在pom.xml中加入以下dependency

dependency.png

 

接著我們建立一個ProjectConfig的Class,

config.png

 

建立Domain Object - Employee

DomainObject.png

 

在我們的test資料庫中建立一個Employee的Table

table.png

 

為Employee建立一個repository,加上add方法insert 至資料庫

repository.png

 

接下來我們來實作一個新增Employee的use case

createEmployeeUseCase.png

 

並且為我們的use case 建立一個input的class

input.png

 

以及在ProjectConfin加上對應的宣告

use case bean.png

 

@Transactional

如果我們沒有在execute方法上加上@Transactional的annotation

當程式即便丟出了RuntimeException,仍會將資料加入到資料庫中

當我們加上@Transactional的annotation,當發生RuntimeException,則會rollback,資料就不會新增到資料庫中

@Transactional 的rollbackFor屬性

這邊特別需要注意的是,如果annotation只有@Transactional

rollback the unchecked exception ( RuntimeExceptio, Error), but does not rollback the cheecked exception

在預設的情況下,transaction只會再發生unchecked exception的時候rollback, 但不會在發生cheecked exception的時候rollback

如果我們希望在發生cheecked exception的時候rollback,我們則可以在@Transactional 加上@Transactional(rollbackFor = Exception.class)

@Transactional 的noRollbackFor屬性

此外,如果你希望某個例外發生不要rollback,該如何做呢? 這時就可以利用 noRollbackFor 屬性,

例如: 我們希望再發生RuntimeException不要rollback,則可以這樣寫 noRollbackFor = RuntimeException.class

 

另外, 只有在public的method才可以使用transaction management

我們也可以把@Transactional的annotation放到Class的宣告上

如此表示該Class的所有public method都會配置相同的trancation屬性訊息

 

最後在我們的Main程式,執行一下我們的use case,測試是否有將資料加到資料庫中

Main.png

接著到我們的資料庫中看看資料是否加進去了

insert one data.png

 

 

@Transactional 的propagation屬性

接著我們要來看propagation屬性,

propagation定義了交易應用於方法上之邊界(Boundaries),它告知何時該開始一個新的交易,或何時交易該被暫停,或者方法是否要在交易中進行。

propagation 屬性.png

propagation屬性預設的狀況是REQUIRED

@Transactional(propagation = Propagation.REQUIRED) → 所以這樣寫其實跟預設的狀態是一樣的,不寫也沒關係

舉幾個例子來看

情境1

repository_transactional.png

以上面這個例子來看,我們先在剛剛上面建立過的EmployeeRepository的add method上面也加上@Transactional

 

createEmployeeUseCase.png

這個時候use case的execute方法呼叫了EmployeeRepository的add方法

只需要在呼叫execute方法時接到add方法中丟出來的RuntimeException,則transaction就會正常的rollback。

 

情境2

use case catch runtime exception.png

接著當我們的EmployeeRepository不變,在use case中我們在呼叫了EmployeeRepository的add方法

並且將add丟出來RuntimeException用try-catch接起來,則會出現如下方的exception

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

這是因為在use case和repository中的@Transactional propagation都採用預設值:REQUREID。

根據REQUIRED特性,當use case調用repository的時候,他們是處於同一個transaction中。

情境2示意圖.png

當repository中拋出了一個例外以後,repository會把當前的transaction標記為需要rollback。

但是use case中捕獲了這個例外,並進行了處理,認為當前transaction應該正常commit。

此時就出現了前後不一致,也就是因為這樣,才會丟出UnexpectedRollbackException。

 

情境3

接著我們的use case不變,修改repository中add方法的propagation為REQUIRES_NEW,如下:

repository_transactional_requires_new.png

此時我們的程式就可以正常的work,不會丟出UnexpectedRollbackException了。

原因是因為當use case調用repository時,repository的add是在一個新的transaction中執行的。

情境3示意圖.png

所以,當repository丟出例外以後,只會把新創建的transaction rollback了,而不會影響到use case的transaction。

use case就可以正常的進行commit。

 

@Transactional 的 isolation屬性

在一個應用程式中,可能有多個交易同時在進行,這些交易應當彼此之間互相不知道另一個交易的存在,

由於交易彼此之間獨立,若讀取的是同一個資料的話,就容易發生問題

Spring提供了幾種隔離層級設定,參考以下表格。預設採用DEFAULT

image

 

@Transactional 的 readOnly屬性

指定交易是否只進行讀取的動作,預設值是false。

像是一些getById, getAll的方法則可以設置@Transactional(readOnly=true)

 

@Transactional 的 timeout屬性

有的交易操作可能延續一段很長的時間,交易本身可能關聯到資料表格的鎖定,因而長時間的交易操作會有效能上的問題,

對於過長的交易操作,您要考慮回滾(Roll back)交易並要求重新操作,而不是無限時的等待交易完成。

預設狀態是-1,表示交易無限制時間。

 

由程式控制Transaction

使用 @Transactional 讓 spring framework 控制 transaction 相當方便,

但是如果今天有什麼特殊的原因需要讓程式控制transaction,該怎麼做?

例如,假設use case邏輯複雜到了一定程度,為了考量程式執行的performance

我不希望我的transaction boundary綁定了整個method,而是在存取repository時,才要開始transaction

因為transaction會跟資料庫鎖定有關係,我們只需要在存取資料庫的時後才做transaction

而其它的程式邏輯,就可以不用在transaction內,如此我們可以提升程式的效能

首先,我們在ProjectConfig先透過constructor injection,將PlatformTransactionManager注入到use case中

以取得 transaction 的管理權

inject txManager to use case.png

接著在use case中,讓use case 自己管理transaction,而不透過 @Transactional

由use case控制transaction.png

這裡只是一個example,說明該如何讓程式自己控制transaction

 

Reference:

https://sites.google.com/site/stevenattw/java/jdbc/spring-transaction-management

https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

https://openhome.cc/Gossip/SpringGossip/TransactionAttribute.html

https://www.itread01.com/content/1524114197.html

https://ithelp.ithome.com.tw/articles/10194749

https://www.cnblogs.com/caoyc/p/5632963.html

arrow
arrow
    創作者介紹
    創作者 Mark Zhang 的頭像
    Mark Zhang

    讀處

    Mark Zhang 發表在 痞客邦 留言(0) 人氣()