簡體   English   中英

為什么即使在Spring服務類的第二種方法中傳播= Propagation.REQUIRES_NEW時,事務也會回滾?

[英]Why are the transactions rolled back even when propagation=Propagation.REQUIRES_NEW in second method in Spring service class?

基本設置現在都很好,我開始嘗試交易。 Struts + Spring + Hibernate注釋事務管理器。 這是Action中的示例代碼,將調用服務類:

    userService.addUser();

這是服務類中的addUser()方法:

    @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        this.addUser2();

    }

首先,我叫addUser在userDAO的,這將插入一個用戶。 其次,我在此服務類的另一個方法中調用了addUser2

    @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
    public void addUser2()   {      
    //should be a new transaction and will not affect the previous one. 
            //this one will fail but should not affect the previous one.
        userDao.addUserFail();        
    }

由於無效PK,這一次將失敗。 我想第二次調用( addUser2 )會失敗,但不會影響前一次調用。 但是,未插入用戶。

如果我只打電話:

   @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        //this.addUser2();      
    }

它正在工作,這意味着像數據庫這樣的基本設置沒有錯。

任何的想法?

Spring的聲明式事務處理使用AOP代理。 當你得到一個tranasactional bean時,你實際上得到一個包裝你的bean實例的代理,攔截方法調用,必要時啟動一個事務,然后調用實際bean的方法,然后在必要時提交或回滾事務。

但是你從同一個bean中的另一個方法調用bean的方法,因此代理被繞過,並且不能應用任何事務行為。

將該方法放在另一個bean中,或者使用AspectJ,它可以檢測字節代碼並攔截bean內方法調用。

有關更詳細的說明,請參閱Spring文檔

我做了一些測試並找到了結論。

  1. 如果第二個服務(內部)是必需的並拋出異常,即使第一個事務捕獲它,它們都會回滾(因為它們在同一條船上!)

  2. 如果第二個服務(內部)是REQUIRES_NEW並拋出異常,外部必須處理這個回滾(這不是我的回滾,但我必須做一些事情),如果沒有捕獲它,外部的這個異常將觸發外部回滾(甚至這不是他的例外,但它是一個例外!)。 所以外部必須為這種情況做一些事情(設置回滾或捕獲它)。

這樣對嗎 ?

這是因為Spring AOP架構。

Spring AOP使用代理,因此只在代理上調用方法時執行方面。

調用this.addUser2(...)您正在調用self對象上的方法,而不是代理。 因此,不執行任何方面,也不執行TX管理。

你可以做以下事情:

  • addUser2方法移動到另一個bean(例如UserService2 ),然后將新bean注入UserService並使用userService2.addUser2()調用該方法。
  • UserService注入UserService (我不確定是否可以完成),並使用userService.addUser2()而不是this.addUser2()調用addUser2() this.addUser2()

就像Amir Pashazadeh說他不知道如何在同一個bean中使用事務上下文調用代理一樣,這是一個例子:

@Component
public class UserService(){     

    @Autowired @Setter private ApplicationContext  applicationContext;
    @Autowired @Setter private UserDao             userDao;

    @Transactional(value="deu"  )
    public void addUser() {     

        userDao.addUser();
        try{
           getProxy().addUser2();
        catch(Exception ex){
           // Avoid rolling back main transaction
           log("OMG it failed!!")
        }
    }

    @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
    public void addUser2()   {      
    //should be a new transaction and will not affect the previous one. 
            //this one will fail but should not affect the previous one.
        userDao.addUserFail();        
    }

    private UserService getProxy() {
        return applicationContext.getBean(UserService.class); 
    }
}

請注意,如果你在addUser2中捕獲了異常,那么Spring似乎會拋出一個UnexpectedRollbackException ,但事務已經標記為Rollback。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM