spring-tx概述 spring-tx包使用註解驅動和AOP通知將事務開啟、提交/回滾、以及複雜的傳播機制封裝了起來,開發者不再需要編寫事務管理的代碼,而是可以只關註自己的業務邏輯。 本文將簡單介紹spring-tx使用步驟以及七種事務傳播級別。 後續文章會閱讀源碼,深入分析spring-tx ...
spring-tx概述
spring-tx包使用註解驅動和AOP通知將事務開啟、提交/回滾、以及複雜的傳播機制封裝了起來,開發者不再需要編寫事務管理的代碼,而是可以只關註自己的業務邏輯。
本文將簡單介紹spring-tx使用步驟以及七種事務傳播級別。
後續文章會閱讀源碼,深入分析spring-tx aop通知、七種事務傳播級別以及事務開啟/提交/回滾的實現方式。
使用步驟
- 導入spring-tx依賴
- 使用@EnableTransactionManagement註解為應用開啟事務支持
- 向spring容器註入一個TransactionManager實現,一般使用DataSourceTransactionManager類
- 在需要事務的業務方法上標註@Transactional註解即可為方法開啟事務通知
Transactional註解參數
- transactionManager - 手動指定要使用的事務管理器
- propagation - 事務傳播級別
- isolation - 事務隔離級別
- timeout - 事務超時時長
- rollbackFor - 發生指定的異常時回滾事務
事務傳播級別
七種級別
-
REQUIRED - Support a current transaction, create a new one if none exists. 支持當前存在的事務,如果當前沒有事務,則新創建一個。預設的傳播級別。
-
SUPPORTS - Support a current transaction, execute non-transactionally if none exists. 支持當前存在的事務,如果當前沒有事務,則在無事務狀態下運行。
For transaction managers with transaction synchronization, SUPPORTS is slightly different from no transaction at all, as it defines a transaction scope that synchronization will apply for. As a consequence, the same resources (JDBC Connection, Hibernate Session, etc) will be shared for the entire specified scope. Note that this depends on the actual synchronization configuration of the transaction manager.
-
MANDATORY - Support a current transaction, throw an exception if none exists. 支持當前存在的事務,如果當前沒有事務,則拋出異常。
-
REQUIRES_NEW - Create a new transaction, and suspend the current transaction if one exists. 創建新事務,如果當前已存在事務則掛起這個事務,再創建新事務。
-
NOT_SUPPORTED - Execute non-transactionally, suspend the current transaction if one exists. 在無事務狀態下運行,如果當前已存在事務則掛起這個事務。
-
NEVER - Execute non-transactionally, throw an exception if a transaction exists. 在無事務狀態下運行,如果當前已存在事務,則拋出異常。
-
NESTED - Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise. 如果當前已存在事務,則嵌入到當前事務中運行,否則和REQUIRED效果一樣。
示例方法
在測試事務傳播級別的示例中,會反覆使用以下6個方法,只是給Transactional添加的參數不同而已,此處記錄一下這幾個方法:
public void insertBlogList(List<Blog> blogList) {
for (int i = 0; i < blogList.size(); i++) {
this.blogMapper.insertBlog(blogList.get(i));
}
}
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
}
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
// 這裡從spring容器獲取service對象,避免事務失效
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
}
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
}
傳播級別詳細說明
REQUIRED
Support a current transaction, create a new one if none exists.
支持當前存在的事務,如果當前沒有事務,則新創建一個。預設的傳播級別。
@Transactional(propagation = Propagation.REQUIRED)
public void insertBlogList(List<Blog> blogList) {
for (int i = 0; i < blogList.size(); i++) {
this.blogMapper.insertBlog(blogList.get(i));
}
}
@Transactional(propagation = Propagation.REQUIRED)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
// 如果調用方法前,沒有事務,則delete操作都會回滾
// 如果調用方法前,已經存在事務,則之前的事務操作都會回滾
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// deleteBlogByCondition方法拋出異常之後,則insertAndDeleteBlogList1方法的操作會回滾
// 如果insertAndDeleteBlogList1方法在另一個事務中,則之前的事務操作都會回滾
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// deleteBlogByCondition方法拋出異常之後,會執行catch代碼塊,之後繼續向下執行,
// 執行blogService.insertBlogList(blogList)方法之後,
// 在commit的時候檢測到insertAndDeleteBlogList2方法rollback-only狀態,會拋出異常:
// org.springframework.transaction.UnexpectedRollbackException:
// Transaction rolled back because it has been marked as rollback-only
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 由於insertAndDeleteBlogList3方法沒有標註Transactional註解:
// blogService.insertBlogList(blogList)方法插入的數據會提交保存下來
// blogService.deleteBlogByCondition(parameter)方法的刪除操作會回滾
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiredTxService blogService =
this.applicationContext.getBean(BlogRequiredTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 由於insertAndDeleteBlogList3方法沒有標註Transactional註解:
// blogService.insertBlogList(blogList)方法插入的數據會提交保存下來
// blogService.deleteBlogByCondition(parameter)方法的刪除操作會回滾
// 之後會繼續執行blogService.insertBlogList(blogList)再插入數據
}
REQUIRES_NEW
Create a new transaction, and suspend the current transaction if one exists.
創建新事務,如果當前已存在事務則掛起這個事務,再打開一個新的資料庫連接創建新事務。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
// 只會回滾delete操作,因為該方法是新創建的事務
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiresNewTxService blogService =
this.applicationContext.getBean(BlogRequiresNewTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// blogService.insertBlogList(blogList)成功
// blogService.deleteBlogByCondition(parameter)會回滾
// 同時insertAndDeleteBlogList1中的事務也會回滾
// 如果使用show processlist查看客戶端進程,
// 可以看到insertBlogList和deleteBlogByCondition方法創建了新的資料庫連接
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiresNewTxService blogService =
this.applicationContext.getBean(BlogRequiresNewTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// blogService.insertBlogList(blogList)成功
// blogService.deleteBlogByCondition(parameter)會回滾
// 此時insertAndDeleteBlogList1中的事務不會回滾,因為deleteBlogByCondition(parameter)的異常被"吞掉了"
// 後續的blogService.insertBlogList(blogList)也會成功
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiresNewTxService blogService =
this.applicationContext.getBean(BlogRequiresNewTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// blogService.insertBlogList(blogList)成功
// blogService.deleteBlogByCondition(parameter)會回滾
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogRequiresNewTxService blogService =
this.applicationContext.getBean(BlogRequiresNewTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// blogService.insertBlogList(blogList)成功
// blogService.deleteBlogByCondition(parameter)會回滾
// 後續的blogService.insertBlogList(blogList)也會成功
}
MANDATORY
Support a current transaction, throw an exception if none exists.
支持當前存在的事務,如果當前沒有事務,則拋出異常。
@Transactional(propagation = Propagation.MANDATORY)
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
// 單獨調用此方法時拋錯:
// org.springframework.transaction.IllegalTransactionStateException:
// No existing transaction found for transaction marked with propagation 'mandatory'
}
@Transactional(propagation = Propagation.MANDATORY)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
// 單獨調用此方法時拋錯:
// org.springframework.transaction.IllegalTransactionStateException:
// No existing transaction found for transaction marked with propagation 'mandatory'
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogMandatoryTxService blogService =
this.applicationContext.getBean(BlogMandatoryTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// deleteBlogByCondition方法拋出異常之後,則insertAndDeleteBlogList1方法的操作會回滾
// 如果insertAndDeleteBlogList1方法在另一個事務中,則之前的事務操作都會回滾
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogMandatoryTxService blogService =
this.applicationContext.getBean(BlogMandatoryTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// deleteBlogByCondition方法拋出異常之後,會執行catch代碼塊,之後繼續向下執行,
// 執行blogService.insertBlogList(blogList)方法之後,
// 在commit的時候檢測到insertAndDeleteBlogList2方法rollback-only狀態,會拋出異常:
// org.springframework.transaction.UnexpectedRollbackException:
// Transaction rolled back because it has been marked as rollback-only
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogMandatoryTxService blogService =
this.applicationContext.getBean(BlogMandatoryTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 拋錯:
// org.springframework.transaction.IllegalTransactionStateException:
// No existing transaction found for transaction marked with propagation 'mandatory'
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogMandatoryTxService blogService =
this.applicationContext.getBean(BlogMandatoryTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 拋錯:
// org.springframework.transaction.IllegalTransactionStateException:
// No existing transaction found for transaction marked with propagation 'mandatory'
}
SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
支持當前存在的事務,如果當前沒有事務,則在無事務狀態下運行。
@Transactional(propagation = Propagation.SUPPORTS)
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
}
@Transactional(propagation = Propagation.SUPPORTS)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
// 單獨調用時刪除操作成功,因為沒有事務
// 如果在一個事務中執行該方法,則會回滾
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogSupportsTxService blogService =
this.applicationContext.getBean(BlogSupportsTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// deleteBlogByCondition方法拋出異常之後,則insertAndDeleteBlogList1方法的操作會回滾
// 如果insertAndDeleteBlogList1方法在另一個事務中,則之前的事務操作都會回滾
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogSupportsTxService blogService =
this.applicationContext.getBean(BlogSupportsTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// deleteBlogByCondition方法拋出異常之後,會執行catch代碼塊,之後繼續向下執行,
// 執行blogService.insertBlogList(blogList)方法之後,
// 在commit的時候檢測到insertAndDeleteBlogList2方法rollback-only狀態,會拋出異常:
// org.springframework.transaction.UnexpectedRollbackException:
// Transaction rolled back because it has been marked as rollback-only
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogSupportsTxService blogService =
this.applicationContext.getBean(BlogSupportsTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 由於insertAndDeleteBlogList3方法沒有開啟事務
// 插入和刪除都會成功,但是deleteBlogByCondition(parameter)還是會拋出異常
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogSupportsTxService blogService =
this.applicationContext.getBean(BlogSupportsTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 由於insertAndDeleteBlogList4方法沒有開啟事務
// 插入和刪除都會成功
}
NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists.
在無事務狀態下運行,如果當前已存在事務則掛起這個事務。
@Transactional
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
// 刪除操作始終都會成功,但還是會拋出異常
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNotSupportedTxService blogService =
this.applicationContext.getBean(BlogNotSupportedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// blogService.deleteBlogByCondition(parameter)的刪除操作成功
// deleteBlogByCondition方法拋出異常之後,則insertAndDeleteBlogList1方法的操作會回滾
// 如果insertAndDeleteBlogList1方法在另一個事務中,則之前的事務操作都會回滾
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNotSupportedTxService blogService =
this.applicationContext.getBean(BlogNotSupportedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 插入數據和刪除操作都會成功
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNotSupportedTxService blogService =
this.applicationContext.getBean(BlogNotSupportedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 插入數據和刪除操作都會成功
// 但是會拋出異常
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNotSupportedTxService blogService =
this.applicationContext.getBean(BlogNotSupportedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 插入數據和刪除操作都會成功
}
NEVER
Execute non-transactionally, throw an exception if a transaction exists.在無事務狀態下運行,如果當前已存在事務,則拋出異常。
@Transactional
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
}
@Transactional(propagation = Propagation.NEVER)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNeverTxService blogService =
this.applicationContext.getBean(BlogNeverTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 插入數據操作回滾
// 因為blogService.deleteBlogByCondition(parameter)檢查到存在事務會拋出異常:
// org.springframework.transaction.IllegalTransactionStateException:
// Existing transaction found for transaction marked with propagation 'never'
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNeverTxService blogService =
this.applicationContext.getBean(BlogNeverTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 兩次插入操作會成功
// 但是刪除操作失敗,因為blogService.deleteBlogByCondition(parameter)檢查到存在事務會拋出異常:
// org.springframework.transaction.IllegalTransactionStateException:
// Existing transaction found for transaction marked with propagation 'never'
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNeverTxService blogService =
this.applicationContext.getBean(BlogNeverTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 插入和刪除都成功
// 但是會拋出異常
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNeverTxService blogService =
this.applicationContext.getBean(BlogNeverTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 插入和刪除都成功
}
NESTED
Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise.
如果當前已存在事務,則嵌入到當前事務中運行,否則和REQUIRED效果一樣。
@Transactional(propagation = Propagation.NESTED)
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
}
@Transactional(propagation = Propagation.NESTED)
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 拋出一個RuntimeException
throw new RuntimeException("deleteBlogByCondition拋出一個異常");
}
@Transactional
public void insertAndDeleteBlogList1(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNestedTxService blogService =
this.applicationContext.getBean(BlogNestedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 所有操作都會回滾
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNestedTxService blogService =
this.applicationContext.getBean(BlogNestedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 前後兩次插入操作成功,
// 中間的刪除操作因為拋出異常,會回滾,
// 又因為blogService.deleteBlogByCondition(parameter)的異常被try...catch了,
// 沒有拋到insertAndDeleteBlogList2中,所以insertAndDeleteBlogList2的操作可以成功提交
}
public void insertAndDeleteBlogList3(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNestedTxService blogService =
this.applicationContext.getBean(BlogNestedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
blogService.deleteBlogByCondition(parameter);
// 插入成功,刪除回滾
}
public void insertAndDeleteBlogList4(List<Blog> blogList, BlogSearchParameter parameter) {
BlogNestedTxService blogService =
this.applicationContext.getBean(BlogNestedTxService.class);
// 插入數據
blogService.insertBlogList(blogList);
// 刪除數據
try {
blogService.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("繼續插入數據");
// 繼續插入數據
blogService.insertBlogList(blogList);
// 插入成功,刪除回滾
}
小結
本文通過示例介紹了spring-tx的七種事務傳播級別,後續的文章將閱讀源碼,分析spring-tx的實現方式。