Spring中同一個service中方法相互調用事務不生效問題解決方案


問題描述:

我們在用Spring框架開發Web項目過程中,經常需要用同一個service中的一個方法調用另一個方法,如果此時調用方沒有添加事務注解@Transactional,而在被調用方添加事務注解@Transactional,當被調用方法中出現異常,這時候會發現事務並沒有回滾,事務注解@Transactional沒有起作用。

分析原因:

我們知道Spring中事務管理是使用AOP代理技術實現的,目標對象自身並沒有事務管理功能的,而是通過代理對象動態增強功能對事務進行增強的。因此當我們在同一個service類中通過一個方法調用另一個方法時,是通過目標對象this對象調用的,目標對象自身並沒有事務管理功能,因此事務不能生效。

下面我們用代碼演示下:

1 public class UserService{
2     ...
3     public User getUserByName(String name) {
4        return userDao.getUserByName(name);
5     }
6     ...

如果配置了事務, 就相當於又創建了一個類:

 1 public class UserServiceProxy extends UserService{
 2     private UserService userService;
 3     ...
 4     public User getUserByName(String name){
 5         User user = null;
 6         try{
 7             // 在這里開啟事務
 8             user = userService.getUserByName(name);
 9             // 在這里提交事務
10         }
11         catch(Exception e){
12             // 在這里回滾事務
13 
14             // 這塊應該需要向外拋異常, 否則我們就無法獲取異常信息了. 
15             // 至於方法聲明沒有添加異常聲明, 是因為覆寫方法, 異常必須和父類聲明的異常"兼容". 
16             // 這塊應該是利用的java虛擬機並不區分普通異常和運行時異常的特點.
17             throw e;
18         }
19         return user;
20     }
21     ...
22 }

 

1 @Autowired
2 private UserService userService;    // 這里spring注入的實際上是UserServiceProxy的對象
3  
4 private void test(){
5     // 由於userService是UserServiceProxy的對象, 所以擁有了事務管理的能力
6     userService.getUserByName("aa");
7 }

Spring事務失效的其他原因

通過對Spring事務代理模式的分析,我們不難發現Spring事務失效的原因有以下幾種情況:

1.private、static、final的使用

解決方法:不在類和方法上使用此類關鍵字

2.通過this.xxx(調用當前類的方法)

使用xml配置方式暴露代理對象.然后在service中通過代理對象AopContext.currentProxy()去調用方法。

xml配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
service調用
 1 @Service
 2 public class HelloWorldServiceImpl implements HelloWorldService {
 3     @Autowired
 4     private BlogRepository blogRepository;
 5 
 6     @Override
 7     public void a(BlogEntity blogEntity) throws Exception {
 8         ((HelloWorldService) AopContext.currentProxy()).b(blogEntity);
 9     }
10 
11     @Transactional(rollbackFor = Exception.class)
12     @Override
13     public void b(BlogEntity blogEntity) throws Exception {
14         blogRepository.save(blogEntity);
15         throw new Exception("錯誤");
16     }
17 }

3.使用默認的事務處理方式

spring的事務默認是對RuntimeException進行回滾,而不繼承RuntimeException的不回滾。因為在java的設計中,它認為不繼承RuntimeException的異常是”checkException”或普通異常,如IOException,這些異常在java語法中是要求強制處理的。對於這些普通異常,spring默認它們都已經處理,所以默認不回滾。可以添加rollbackfor=Exception.class來表示所有的Exception都回滾。

4.線程Thread中聲明式事務不起作用

 1 @Override
 2     public void run() {
 3         DefaultTransactionDefinition def = new DefaultTransactionDefinition();
 4         def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 5         PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class);
 6         TransactionStatus status = txManager.getTransaction(def);
 7         try {
 8                testDao.save(entity);
 9                txManager.commit(status); // 提交事務
10             } catch (Exception e) {
11                 System.out.println("異常信息:" + e.toString());
12                 txManager.rollback(status); // 回滾事務
13             }                        
14     }
 
        

從上面代碼可以看出,我們的解決方案是使用了編程式事務。

5.配置的事務與掃描的service不在同一個容器

 在spring-framework-reference.pdf文檔中有這樣一段話:

<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services. 

    這句話的意思是,<tx:annoation-driven/>只會查找和它在相同的應用上下文件中定義的bean上面的@Transactional注解,如果你把它放在Dispatcher的應用上下文中,它只檢查控制器上的@Transactional注解,而不是你services上的@Transactional注解。

    如果將事務配置定義在Spring MVC的應用上下文(*-servlet.xml)中,在Controller上的@Transactional注解是可以起作用的。而services上的@Transactional注解將不起作用。

 

詳情可見:https://www.cnblogs.com/xiaojiesir/p/11058541.html

6.方法配置的事務傳播行為有問題

被調用方法的事務傳播行為設置為PROPAGATION_REQUIRES_NEW,導致產生兩個獨立的事務,外圍方法拋出異常只回滾和外圍方法同一事務的方法。

詳情可見:https://segmentfault.com/a/1190000013341344


免責聲明!

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



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