日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Spring事務失效的各種場景

一、訪問權限

JAVA的訪問權限主要是:private、default、protected、public,它們的權限則是依次變大。
如果我們在開發的時候定義錯誤的訪問權限,就會導致事務出現問題

@Service
public class DemoService {
    
    @Transactional
    private void query(Demo demo) {

    }
}

 

我們可以查看源碼,可以明白,spring事務的實現
AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則TransactionAttribute返回null,即不支持事務。

二、方法用final修飾

我們在學習的時候都知道,某個方法不想被子類繼承的時候就會加上關鍵字final,這種寫法正常來說是沒有問題的,但是如果該方法被用上事務就不行了

@Service
public class DemoService {

    @Transactional
    public final void query(Demo demo) {
      
    }
}

這樣的話會導致事務的失效,因為spring事務底層實現使用了代理,aop,通過jdk的動態代理或者cglib,生成了代理類,在代理類中實現了事務功能,如果方法被final修飾,無法重寫該方法,也就無法添加事務的功能了


 

 

像idea這里就提出了提示,同樣的如果方法是static方法也是不行的


 

 

因為靜態方法是屬于類的,而不是屬于對象的,無法重寫靜態方法所以也就不可能實現事務

三、方法內部調用

在同一個類的service中,調用其他的事務方法

@Service
public class DemoService {

    @Transactional
    public  void query(Demo demo) {
        save(demo);
    }
    
    @Transactional
    public void save(Demo demo) {
        
    }
    
}

可以看到,query方法調用了save的方法,由于spring的事務實現是因為aop生成代理,這樣是直接調用了this對象,所以也不會生成事務。

解決方法:
1、增加一個service,把一個事務的方法移到新增加的service方法里面,然后進行注入再調用

@Service
public class DemoTwoService {
    
    @Transactional
    public void save(Demo demo) {

    }
}

@Service
public class DemoService {

    @Autowired
    DemoTwoService demoTwoService;

    @Transactional
    public  void query(Demo demo) {
        demoTwoService.save(demo);
    }
}

2、在自己類中注入自己

@Service
public class DemoService {

    @Autowired
    DemoService demoService;

    @Transactional
    public  void query(Demo demo) {
        demoService.save(demo);
    }

    @Transactional
    public void save(Demo demo) {

    }
}

由于這種寫法基于spring的三級緩存不會導致,循環依賴的問題出現

3、通過AopContentent

@Service
public class DemoService {
    
    @Transactional
    public  void query(Demo demo) {
        DemoService demoService = (DemoService)AopContext.currentProxy();
        demoService.save(demo);
    }

    @Transactional
    public void save(Demo demo) {

    }
}

四、沒有被spring管理

在使用spring事務的時候,對象要被spring進行管理,也就是需要創建bean,一般我們都會加@Controller、@Service、@Component、@Repository等注解,可以自動實現bean實例化和依賴注入的功能。,如果忘記加了,也會導致,事務的失效

五、多線程調用

@Service
public class DemoService {

    @Autowired
    DemoTwoService demoTwoService;

    @Transactional
    public  void query(Demo demo) {
        new Thread(()->{
            demoTwoService.save(demo);
        }).start();
    }
}

通過上面的例子,我們可以知道,query調用了事務方法save,但是事務方法在另一個線程里面調用,這樣會導致兩個方法在不同的一個線程中,獲取的數據庫連接也不一樣,所以會是兩個不同的事務,如果save的方法拋出了異常,query回滾是不可能的,看過spring源碼,我們可以知道,spring的事務是通過連接數據庫來實現的,當前線程保存了一個map,key—數據源,value----數據庫連接,事務其實就是指向同一個連接的,只有擁有同一個數據庫連接才能同時提交和回滾,如果在不同的線程,數據庫的連接不是同一個,所以事務也不是同一個。

private static final ThreadLocal<Map<Object, Object>> resources =

  new NamedThreadLocal<>("Transactional resources");

六、設計的表不支持事務

我們都知道,MySQL5之前默認的數據庫引擎是MyISAM,可能一些老的項目還在使用,但是他是不支持事務的

七、沒有開啟事務

如果創建的不是springboot項目可能會導致這樣的問題出現,因為springboot項目有自動裝配的類
DataSourceTransactionManagerAutoConfiguration,已經默認開啟了事務,配置spring.datasource參數就行,如果是spring項目,需要在ApplicationContext.xml配置的,不然事務不會生效

<!-- 配置事務管理器 --> 
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean> 
<tx:advice id="advice" transaction-manager="transactionManager"> 
    <tx:attributes> 
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes> 
</tx:advice> 
<!-- 用切點把事務切進去 --> 
<aop:config> 
    <aop:pointcut expression="execution(* com.demo.*.*(..))" id="pointcut"/> 
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
</aop:config> 

還有就是切點配錯,也會導致事務失效

八、錯誤的事務傳播

我們在使用@Transactional注解時,是可以指定propagation參數的。
該參數的作用是指定事務的傳播特性,
spring目前支持7種傳播特性:

• REQUIRED 如果當前上下文中存在事務,那么加入該事務,如果不存在事務,創建一個事務,這是默認的傳播屬性值。
• SUPPORTS 如果當前上下文存在事務,則支持事務加入事務,如果不存在事務,則使用非事務的方式執行。
• MANDATORY 如果當前上下文中存在事務,否則拋出異常。
• REQUIRES_NEW 每次都會新建一個事務,并且同時將上下文中的事務掛起,執行當前新建事務完成以后,上下文事務恢復再執行。
• NOT_SUPPORTED 如果當前上下文中存在事務,則掛起當前事務,然后新的方法在沒有事務的環境中執行。
• NEVER 如果當前上下文中存在事務,則拋出異常,否則在無事務環境上執行代碼。
• NESTED 如果當前上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務。
如果我們在手動設置propagation參數的時候,把傳播特性設置錯了

@Service
public class DemoService {

    @Transactional(propagation = Propagation.NEVER)
    public  void query(Demo demo) {

    }
}

我們可以看到query的事務傳播設置為了Propagation.NEVER,這種類型的傳播不支持事務,會拋異常,
目前支持事務的三種傳播特性為:REQUIRED,REQUIRES_NEW,NESTED

九、自己捕獲了異常

事務不回滾,可能是我們在寫代碼的時候自己在代碼手動進行了try…catch

@Transactional
    public  void query(Demo demo) {
        try {
            save(demo);
        } catch (Exception e) {
            System.out.println("異常");
        }
    }

這種情況下,spring事務不會進行回滾,因為我們進行了手動捕獲異常,然后沒有手動拋出,如果想要spring事務的正常回滾,必須拋出它能處理的異常,如果沒有拋出異常,spring會認為程序沒有問題。

十、手動拋出別的異常

我們沒有手動捕獲異常,但是如果拋出的異常不正確,spring事務也不會回滾。

 @Transactional
    public  void query(Demo demo) throws Exception{
        try {
            save(demo);
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

上面我們捕獲了異常,然后手動拋出Exception,事務同樣不會回滾,因為spring事務,默認情況下不會回滾Exception(非運行時的異常),只會回滾RuntimeException(運行時異常)和Error(錯誤)。

十一、自定義回滾異常

在使用@Transactional注解聲明事務時,有時我們想自定義回滾的異常,spring也是支持的。可以通過設置rollbackFor參數,來完成這個功能。

但如果這個參數的值設置錯了,就會引出一些問題

  @Transactional(rollbackFor = BusinessException.class)
    public  void query(Demo demo) throws Exception{
            save(demo);
    }

上面是我們自定義的業務異常,如果在執行上面這段代碼,保存和更新數據時,程序報錯了,拋了SqlException、DuplicateKeyException等異常。而BusinessException是我們自定義的異常,報錯的異常不屬于BusinessException,所以事務也不會回滾。即使rollbackFor有默認值,但阿里巴巴開發者規范中,還是要求開發者重新指定該參數。
因為如果使用默認值,一旦程序拋出了Exception,事務不會回滾,這會出現很大的bug。所以,建議一般情況下,將該參數設置成:Exception或Throwable。

十二、嵌套事務回滾過頭

@Service
public class DemoService {

    @Autowired
    private DemoTwoService demoTwoService;

    @Autowired
    private DemoDao demoDao;
    public
    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
        demoDao.save(demo);
        demoTwoService.save(demo);
    }
}

原本只是希望回滾demoServise.save(),不回滾demoDao.save(demo);,但是這種情況兩個都會被回滾,因為demoTwoService.save(demo);沒有捕獲異常,往上拋出,導致query進行回滾,所以同時回滾了兩個
解決方法:

@Service
public class DemoService {

    @Autowired
    private DemoTwoService demoTwoService;

    @Autowired
    private DemoDao demoDao;
    public
    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
        demoDao.save(demo);
        try {
            demoTwoService.save(demo);
        } finally {
            
        }
    }
}

可以將內部嵌套事務放在try/catch中,并且不繼續往上拋異常。這樣就能保證,如果內部嵌套事務中出現異常,只回滾內部事務,而不影響外部事務。

十三、編程式事務

上面的事務失效都是基于@Transactional注解的,我們把這種事務叫做:聲明式事務。

其實,spring還提供了另外一種創建事務的方式,即通過手動編寫代碼實現的事務,我們把這種事務叫做:編程式事務。

@Service
public class DemoService {
    

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
       transactionTemplate.execute((status -> {
           save(demo);
           return Boolean.TRUE;
       }));
    }

    @Transactional
    public void save(Demo demo) {

    }
}

在spring中為了支持編程式事務,專門提供了一個類:TransactionTemplate,在它的execute方法中,就實現了事務的功能。

使用TransactionTemplate的編程式事務可以

• 避免由于spring aop問題,導致事務失效的問題。
• 能夠更小粒度的控制事務的范圍。
tips:關于本文舉例的query加事務不太符合開發的要求,但是有點懶就不改了

分享到:
標簽:Spring
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定