Spring七大事務(wù)傳遞機制深入分析實現(xiàn)原理
Spring事務(wù)傳遞機制原理
首先,我們通過org.springframework.transaction.annotation.Propagation來了解一下spring事務(wù)的傳播定義:
1. REQUIRED(默認(rèn)):
Support a current transaction, create a new one if none exists.
支持當(dāng)前事務(wù),如果沒有則創(chuàng)建一個新的
2. SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
支持當(dāng)前事務(wù),如果沒有則不使用事務(wù)
3. MANDATORY
Support a current transaction, throw an exception if none exists
支持當(dāng)前事務(wù),如果沒有事務(wù)則報錯
4. REQUIRED_NEW
Create a new transaction, and suspend the current transaction if one exists.
新建一個事務(wù),同時將當(dāng)前事務(wù)掛起
5. NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists
以無事務(wù)的方式執(zhí)行,如果當(dāng)前有事務(wù)則將其掛起
6. NEVER
Execute non-transactionally, throw an exception if a transaction exists.
以無事務(wù)的方式執(zhí)行,如果當(dāng)前有事務(wù)則報錯
7. NESTED
Execute within a nested transaction if a current transaction exists,behave like PROPAGATION_REQUIRED else
如果當(dāng)前有事務(wù),則在當(dāng)前事務(wù)內(nèi)部嵌套一個事務(wù),內(nèi)部事務(wù)的回滾不影響當(dāng)前事務(wù)。如果當(dāng)前沒有事務(wù),就相當(dāng)于REQUIRED
Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to
the JDBC DataSourceTransactionManager when working on a JDBC 3.0 driver.
Some JTA providers might support nested transactions as well.
注意:該定義只能在JDBC3.0驅(qū)動下的DataSourceTransactionManager事務(wù)管理器中使用,有些JTA事務(wù)可能也會支持
接下來我們通過代碼驗證一下spring事務(wù)的傳遞性,在UserServiceImpl類添加兩個方法如下:
@Transactional(propagation = Propagation.NEVER)
public User findById(Long id) {
User user = userMapper.findById(id);
System.out.println("find user:"+user);
return user;
}
@Transactional
public void transactionTest(int t) {
findById(t+0L);
}我們調(diào)用transactionTest方法,transactionTest沒有配置Propagation,所以默認(rèn)是REQUIRED,會在當(dāng)前新建一個事務(wù)。transactionTest內(nèi)部調(diào)用findById,由于findById事務(wù)傳播定義為NEVER,表明它當(dāng)前不能有事務(wù),按理說這里會拋出異常,但是我們利用junit執(zhí)行后發(fā)現(xiàn),transactionTest是可以正常執(zhí)行的。
事實上,如果使用@Transaction方法里嵌套調(diào)用的是同一個類的方法,spring代理會忽略嵌套方法的@Transaction配置。但是,如果是其他注入對象的方法,那么@Transaction配置就會生效。我們將上面的transactionTest方法的事務(wù)傳播定義為NERVER,并新增一個insert操作,即使insert啟用了事務(wù)并且拋出異常,但是事務(wù)不會生效,也不會有回滾的說法,程序會拋出異常但是數(shù)據(jù)會保存到數(shù)據(jù)庫中:
@Transactional(propagation = Propagation.NEVER)
public void transactionTest(int t) {
findById(t+0L);
insertUser("huangxl","abc123");
}
@Transactional
public int insertUser(String name, String password) {
User user = new User();
user.setPassword(password);
user.setUsername(name);
int insertCount = userMapper.insertEntity(user);
if(insertCount == 1 ){
throw new RuntimeException("test transaction roll back");
}
return insertCount;
}
接下來我們來測試不同類之間的方法(事務(wù))調(diào)用,以下的測試都是基于junit執(zhí)行TransactionTestServiceImpl.test()方法
一、Propagation.NERVER的測試
下面我們將UserService注入到TransactionTestServiceImpl中,test方法使用@Transactional,UserService findById事務(wù)傳播定義不變,還是NERVER。
UserserviceImpl:
@Service
public class TransactionTestServiceImpl implements TransactionTestService {
@Autowired
private UserService userService;
@Override
@Transactional
public void test() {
userService.findById(1L);
}
}
TransactionTestServiceImpl:
@Service
public class UserServiceImpl implements UserService {
@Override
@Transactional(propagation = Propagation.NEVER)
public User findById(Long id) {
User user = userMapper.findById(id);
System.out.println("find user:"+user);
return user;
}
}
由于test默認(rèn)啟用了事務(wù),findById不允許當(dāng)前有事務(wù),所以我們執(zhí)行test方法后會發(fā)現(xiàn)程序拋出了異常:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’
結(jié)論:
NERVER 不允許當(dāng)前存在事務(wù)
二、Propagation.REQUIRED的測試
UserserviceImpl:
@Transactional
public int insertUser(String name, String password) {
User user = new User();
user.setPassword(password);
user.setUsername(name);
int insertCount = userMapper.insertEntity(user);
if(insertCount == 1 ){
throw new RuntimeException("test transaction roll back");
}
return insertCount;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
try {
userService.insertUser("abc", "123");
} catch (Exception e) {
//do Nothing
}
userMapper.updateUserPassWord(1L, "456");
}
我們會發(fā)現(xiàn),即使捕獲了userService.insertUser拋出的異常,test還是把insertUser和updateUserPassword操作當(dāng)成是一個整體,整個事務(wù)還是回滾了,程序拋出了下面的異常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
結(jié)論:
REQUIRED子事務(wù)會影響當(dāng)前事務(wù)的提交、回滾
三、Propagation.NESTED的測試
UserserviceImpl:
@Transactional(propagation = Propagation.NESTED)
public int insertUser(String name, String password) {
User user = new User();
user.setPassword(password);
user.setUsername(name);
int insertCount = userMapper.insertEntity(user);
if(insertCount == 1 ){
throw new RuntimeException("test transaction roll back");
}
return insertCount;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
try {
userService.insertUser("abc", "123");
} catch (Exception e) {
//do Nothing
}
userMapper.updateUserPassWord(1L, "456");
}
程序正常運行,因為NESTED內(nèi)部事務(wù)回滾不影響外部事務(wù)。假如這個時候我們把test的@Transactional去掉再運行test方法,發(fā)現(xiàn)insertUser沒有插入用戶信息,說明當(dāng)前沒有事務(wù)的情況下,NESTED會默認(rèn)創(chuàng)建一個事務(wù),類似于REQUIRED。
如果我們把程序改為下面的情況:
UserserviceImpl:
@Transactional(propagation = Propagation.NESTED)
public int insertUser(String name, String password) {
User user = new User();
user.setPassword(password);
user.setUsername(name);
int insertCount = userMapper.insertEntity(user);
return insertCount;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
userService.insertUser("abc", "123");
int updateRow = userMapper.updateUserPassWord(1L, "456");
if (updateRow == 1) {
throw new RuntimeException("transational roll back");
}
}
我們會發(fā)現(xiàn)沒有插入用戶信息,當(dāng)前事務(wù)和子事務(wù)全部回滾。
結(jié)論:
NESTED子事務(wù)回滾不會影響當(dāng)前事務(wù)的提交(catch回滾異常的情況下),但是當(dāng)前事務(wù)回滾會回滾子事務(wù)。也就是說只有當(dāng)前事務(wù)提交成功了,子事務(wù)才會提交成功。
四、Propagation.REQUIRED_NEW的測試
UserserviceImpl:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int insertUser(String name, String password) {
User user = new User();
user.setPassword(password);
user.setUsername(name);
int insertCount = userMapper.insertEntity(user);
return insertCount;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
userService.insertUser("abc", "123");
int updateRow = userMapper.updateUserPassWord(1L, "456");
if (updateRow == 1) {
throw new RuntimeException("transational roll back");
}
}
運行結(jié)果:程序報錯,但是有用戶信息插入。
將程序改為下面的樣子:
UserserviceImpl:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int updateUserPassWorld(Long id, String password) {
int update = userMapper.updateUserPassWord(id,password);
return update;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
//當(dāng)前事務(wù)
userMapper.updateUserPassWord(28L, "123456");
//執(zhí)行REQUIRES_NEW事務(wù)
userService.updateUserPassWorld(28L, "000000");
System.out.println("commit");
}
執(zhí)行程序,發(fā)現(xiàn)程序遲遲沒有打印字符串commit,發(fā)生了死鎖。
結(jié)論:
REQUIRES_NEW會啟用一個新的事務(wù),事務(wù)擁有完全獨立的能力,它不依賴于當(dāng)前事務(wù),執(zhí)行時會掛起當(dāng)前事務(wù),直到REQUIRES_NEW事務(wù)完成提交后才會提交當(dāng)前事務(wù),如果當(dāng)前事務(wù)與REQUIRES_NEW 存在鎖競爭,會導(dǎo)致死鎖。
五、NOT_SUPPORTED的測試
UserserviceImpl:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int updateUserPassWorld(Long id, String password) {
int updateRow = userMapper.updateUserPassWord(id,password);
if(updateRow ==1 ){
throw new RuntimeException("roll back test");
}
return updateRow;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
userService.updateUserPassWorld(28L, "000000");
}
程序運行報錯,但是id為28的用戶密碼還是更新了。
將程序改為下面這個情況:
UserserviceImpl:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int updateUserPassWorld(Long id, String password) {
int update = userMapper.updateUserPassWord(id,password);
return update;
}
TransactionTestServiceImpl:
@Transactional
public void test() {
//當(dāng)前事務(wù)
userMapper.updateUserPassWord(28L, "123456");
//執(zhí)行REQUIRES_NEW事務(wù)
userService.updateUserPassWorld(28L, "000000");
System.out.println("commit");
}
執(zhí)行程序,發(fā)現(xiàn)程序遲遲沒有打印字符串commit,發(fā)生了死鎖。
結(jié)論:
NOT_SUPPORTED會掛起當(dāng)前事務(wù),并且NOT_SUPPORTED定義的方法內(nèi)部不啟用顯示事務(wù),如果NOT_SUPPORTED和當(dāng)前事務(wù)存在鎖競爭,會發(fā)生死鎖。
六、NOT_SUPPORTED的測試
UserserviceImpl:
@Transactional(propagation = Propagation.MANDATORY)
public int updateUserPassWorld(Long id, String password) {
int updateRow = userMapper.updateUserPassWord(id,password);
return updateRow;
}
TransactionTestServiceImpl:
public void test() {
userService.updateUserPassWorld(28L, "123456");
}
程序運行錯誤:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’
結(jié)論:
MANDATORY必須包含在事務(wù)中,如果事務(wù)不存在,則拋出異常
到此這篇關(guān)于Spring七大事務(wù)傳遞機制深入分析實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Spring事務(wù)傳遞機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Apache Commons fileUpload實現(xiàn)文件上傳之一
這篇文章主要介紹了Apache Commons fileUpload實現(xiàn)文件上傳之一的相關(guān)資料,需要的朋友可以參考下2016-03-03
詳解Spring Cloud Alibaba Sidecar多語言微服務(wù)異構(gòu)
這篇文章主要介紹了詳解Spring Cloud Alibaba Sidecar多語言微服務(wù)異構(gòu),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
詳解rabbitmq創(chuàng)建queue時arguments參數(shù)注釋
這篇文章主要介紹了rabbitmq創(chuàng)建queue時arguments參數(shù)注釋,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
java中同類對象之間的compareTo()和compare()方法對比分析
這篇文章主要介紹了java中同類對象之間的compareTo()和compare()方法對比分析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09

