【springboot】事務處理


轉自:

  https://blog.csdn.net/cp026la/article/details/86496788

扯淡:

  復雜的業務邏輯中一個請求可能需要多次操作數據庫,要保證一個Service 方法中多個 dao 的操作同時成功(失敗),事務的配置就很重要了。

大概分三種情況:

  1、分布式事務:即多模塊中事務,分布式事務建議是可以避免就避免,可以使用消息中間件處理,但也不能完全解決。
  2、多線程事務:參考:https://blog.csdn.net/kongkongyanan/article/details/81703415
  3、單模塊中的事務,本章的重點,也是開發中遇到的最多的,這里給出兩種配置方式。

  本章分別使用注解 @Transactional (springboot默認推薦)和 AOP 全局配置的方式:

pom 依賴(延續上一章代碼)增加aop的依賴:

<!-- aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 

一、事務演示:

需要引入spring-boot-starter-jdbc依賴,但是我們集成了mybatis,已經包含了該依賴,不必重復引入。
1、controller層代碼:在UserController中添加測試的方法

 @GetMapping
 @RequestMapping(value = "add/{number}")
 public ApiResult add(@PathVariable("number") Integer number){
      userService.addUser(number);
      return ApiResult.ok();
 }

2、service層代碼:在UserService中添加測試方法,設置一個可控的除以0異常

    public void addUser(int number){
        User user = new User();
        user.setId(22);
        user.setName("xiaoming");
        user.setAge(12);
        mapper.insertSelective(user);

        int num = 10/number;

        User user2 = new User();
        user2.setId(23);
        user2.setName("xiaohong");
        user2.setAge(90);
        mapper.insertSelective(user2);
    }  

3、測試
3.1、請求接口:http://localhost:8080/user/add/10 數據庫正常插入兩條數據

3.2、刪除剛插入的兩條數據。請求接口:http://localhost:8080/user/add/0 制造除以0 異常。
現象:數據庫此時只插入第一條數據(xiaoming)。
分析:插入第二條數據(xiaohong)之前,出現除以0異常,造成程序中斷。

需求: 這種在同一個邏輯中的多個dao操作要么同時成功、要么同時失敗,不允許只成功一個dao操作的情況出現。

二、使用@Transactional 配置事務:

1、在service層addUser 方法上添加@Transactional注解

    @Transactional(rollbackFor = Exception.class)
    public void addUser(int number){
       ...
    }

 

2、測試:刪除數據庫中已經成功插入的數據。
2.1、請求接口:http://localhost:8080/user/add/0 制造除以0 異常。
2.2、結果:出現除以0 異常,數據庫並未插入任何數據,即實現了多個dao操作同時成功,同時失敗。

3、注意點:

1、spring 事務默認回滾的是運行時異常(RuntimeException)和錯誤(Error),非檢測異常,例如SQLException 不會回滾。加上rollbackFor = Exception.class 可解決。
2、在service中使用了try catch 捕獲了異常,事務不會回滾,因為try catch 異常之后就相當於沒有異常,建議異常在controller中統一處理。

三、使用 AOP 全局處理事務:

需要引入 aop 的依賴。

1、新建全局事務配置類(個人放置到config包中,方便管理):

/**
 * spring aop 配置全局事務
 */
@Aspect
@Configuration
public class TransactionAdviceConfig {

    /*
     * 定義切入點
     * execution()是最常用的切點函數
     * execution (com.coolron.user.service.impl..*.*(..))
     * 1、execution(): 表達式主體。
     * 2、第一個*號:表示返回類型,*號表示所有的類型。
     * 3、包名:表示需要攔截的包名,后面的兩個句點表示當前包和當前包的所有子包,com.sample.service.impl包、子孫包下所有類的方法。
     * 4、第二個*號:表示類名,*號表示所有的類。
     * 5、*(..):最后這個星號表示方法名,*號表示所有的方法,后面括弧里面表示方法的參數,兩個句點表示任何參數。
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.coolron.user.service.impl..*.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Bean
    public TransactionInterceptor txAdvice() {

        DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
        txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttr_REQUIRED_READONLY.setReadOnly(true);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        // service 中方法前綴
        source.addTransactionalMethod("add*", txAttr_REQUIRED);
        source.addTransactionalMethod("save*", txAttr_REQUIRED);
        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
        source.addTransactionalMethod("update*", txAttr_REQUIRED);

        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }

}  

2、測試:
將 Service 中addUser 方法上的 @Transactional 注解注釋,刪除數據。
2.1、請求接口:http://localhost:8080/user/add/10 正常操作,數據庫插入兩條數據。
2.2、刪除剛插入的兩條數據。請求接口:http://localhost:8080/user/add/0 制造除以0 異常。
結果:出現異常,數據庫並未插入數據。

3、注意點:

1、正確配置切入點的位置,本章配置的是:
execution(* com.coolron.user.service.impl….(…)),也可將user換成 * 指定多個位置
2、service 層中方法名應嚴格按照全局事務配置類中定義的規則來命名。
3、try catch 同樣也不要在service中使用。

至此,單模塊中的事務配置已完成,也是平時開發中最遇到的,對於分布式事務(即多模塊),盡量將涉及到事務的業務邏輯放到一個模塊中處理,使用消息中間件處理也不是最佳方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM