spring的事務註解@Transaction 相信不少人都用過,而@Transaction 默認配置適合80%的配置。java
本篇文章不是對spring註解事務作詳細介紹,而是解決一些實際場景下遇到的問題spring
spring事務註解的基本原理數據庫
下面針對是否須要開啓事務和是否須要回滾事務在特定場景下的介紹ide
@Transactional public void rollback() throws SQLException { // update db throw new SQLException("exception"); }
上述代碼事務會回滾嗎?不會的,就算拋出SQLException了,可是以前的數據庫操做依然會提交,緣由就是@Transactional默認狀況下只回滾RuntimeException和Error。函數
所以,若是要指定哪些異常須要回滾,則經過配置@Transactional中rollbackFor,例如spa
@Transactional(rollbackFor = {SQLException.class}) public void rollback() throws SQLException { // update db throw new SQLException("exception"); }
按照上面例子,那指定的SQLException,當拋出RuntimeException的時候,還會回滾嗎?.net
@Transactional(rollbackFor = {SQLException.class}) public void rollback() throws SQLException { // update db throw new Runtime("exception"); }
假設有下面的邏輯,事務會回滾嗎3d
@Transactional public void rollback() { // updateA try{ selfProxy.innelTransaction() }catch(RuntimeException e){ //do nothing } //updateC } @Transactional public void innelTransaction() throws SQLException { // updateB throw new RuntimeException("exception"); }
答案是會回滾,由於內部事務觸發回滾,當前事務被標記爲 rollback-only,代理
當外部事務提交的時候,Spring拋出如下異常,同時回滾外部事務code
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
因此,在須要事務回滾的時候,最好仍是拋出RuntimeException,而且不要在代碼中捕獲此類異常
@Transaction中的propagation的能夠配置事務的傳播性,網上介紹的不少,就直接複製一段
PROPAGATION_REQUIRED--支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇。 (也是默認策略) PROPAGATION_SUPPORTS--支持當前事務,若是當前沒有事務,就以非事務方式執行。 PROPAGATION_MANDATORY--支持當前事務,若是當前沒有事務,就拋出異常。 PROPAGATION_REQUIRES_NEW--新建事務,若是當前存在事務,把當前事務掛起。 PROPAGATION_NOT_SUPPORTED--以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。 PROPAGATION_NEVER--以非事務方式執行,若是當前存在事務,則拋出異常。
有時候須要在一個事務中,讀取最新數據(默認是讀取事務開始前的快照)。其實很簡單,只要使用上面PROPAGATION_NOT_SUPPORTED傳播性就能夠了。
@Transactional public void transaction() throws SQLException { // do something selfProxy.queryNewValue(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void queryNewValue() throws SQLException { //查詢數據中的最新值 }
事務註解的實質就是在建立一個動態代理,在調用事務方法前開啓事務,在事務方法結束之後決定是事務提交仍是回滾。
所以,直接在類內部中調用事務方法,是不會通過動態代理的
所以,若是要使方法B點事務生效,必須經過代理類調用,
解決思路:須要在內部調用方法B的時候,找到當前類的代理類,用代理類去調用方法B
@Service public class MyService{ @Transactional public void transaction(){ // do something ((MyService) AopContext.currentProxy()).queryNewValue(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void queryNewValue(){ //查詢數據中的最新值 } }
經過AopContext.currentProxy()能夠拿到當前類的代理類,可是要使用這個時候,必須在啓動類上加上
@EnableAspectJAutoProxy(exposeProxy=true)
既然是要拿到當前代理類,那其實直接在Spring的容器裏面去拿也能夠啊。在spring中拿Bean的方法大體有2種
@Service public class MyService{ @Autowired private MyService self; @Transactional public void transaction() { // do something self.queryNewValue(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void queryNewValue() { //查詢數據中的最新值 } }
tips:spring如今對一些循環依賴是提供支持的,簡單來講,知足:
@Service public class MyService implements BeanFactoryAware{ private MyService self; @Transactional public void transaction(){ // do something self.queryNewValue(); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void queryNewValue() { //查詢數據中的最新值 } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { self = beanFactory.getBean(MyService.class); } }