6

我有一個數據訪問類,它作爲獨立Java應用程序的一部分運行。它目前正在工作,這意味着一個事務管理器被定義,但我想重構該類以減少事務的範圍,但是如果我這樣做,我會得到org.hibernate.HibernateException:沒有Hibernate會話綁定到線程,並且配置沒有允許在這裏創建非事務性的一個,這意味着移動@Transactional已經以某種方式阻止了它被識別。你如何重構@Transactional方法來拆分非事務性部分

我原來的版本有重構的方法是私人的,但我找到了一個建議,將其改爲public,因爲在某些情況下,註釋不會被拾取。

public class DoStuff { 
    @Transactional 
    public void originalMethod() { 
     // do database stuff 
     ... 

     // do non-database stuff that is time consuming 
     ... 
    } 
} 

我想要做的是重構以下

public class DoStuff { 
    public void originalMethod() { 
     doDatabaseStuff() 

     doNonDatabaseStuff() 
    } 

    @Transactional 
    public void doDatabaseStuff() { 
     ... 
    } 

    public void doNonDatabaseStuff() { 
     ... 
    } 
} 
+0

你的交易經理在哪裏?你能添加更多細節嗎? – Chris

+0

你的類DoStuff是否實現了任何接口? –

+0

沒有接口,並且事務管理器被定義爲它對原始類的作用。 –

回答

6

編輯:

您需要了解how Spring proxying works理解爲什麼你的重構不起作用。

對象引用的方法調用是代理上的調用,因此代理將能夠委託給與該特定方法調用相關的所有攔截器(通知)。但是,一旦調用最終到達目標對象,它將自己創建的任何方法調用將針對此引用而不是代理進行調用。這具有重要的意義。這意味着自我調用不會導致與方法調用相關的建議獲得執行機會。

@Transactional使用Spring AOP,Spring使用代理。這意味着當你從另一個類中調用@Transactional方法時,Spring將使用代理,所以事務性的建議將被應用。但是,如果您從同一個類中調用方法,spring將使用「this」引用而不是代理,這樣事務性建議將不會被應用。

原來的答案:

下面是我在類似的方案什麼工作。

public class DoStuff implement ApplicationContextAware {  
private ApplicationContext CONTEXT; 
public void setApplicationContext(ApplicationContext context) throws BeansException { 
    CONTEXT = context; 
} 

    public void originalMethod() {   
     getSpringProxy().doDatabaseStuff()    
     doNonDatabaseStuff()  
    } 

    private DoStuff getSpringProxy() { 
     return context.getBean(this.getClass());  
    } 
    @Transactional  
    public void doDatabaseStuff() {   
     ...  
    }   

    public void doNonDatabaseStuff() {   
     ...  
    } 
} 

說明:

  1. 使類ApplicationContextAware,所以它的上下文的引用
  2. 當你需要調用一個事務性方法,從上下文提取實際的春天代理
  3. 使用此代理來調用您的方法,以便實際應用@Transactional。
+0

原始類已識別@Transactional並能正常工作。它只在重構後停止工作。 –

+0

@Michael Rutherfurd看到我的編輯 – gresdiplitude

+0

這似乎是我的問題。謝謝 –

0

你的方法看起來應該很好,我期待這個問題與Spring代理有關。

我詢問接口的原因與Spring應用事務行爲(JDK動態代理)的默認方法有關。

如果你的類的實際定義是:

public class DoStuff implements Doable { 
    public void originalMethod() { 

    } 
} 

public interface Doable { 
    public void originalMethod(); 
} 

如果這確實是結構,當你移動到新的結構春天是不能代理新doDatabaseStuff方法。

你的選項,以解決此問題:

  • 添加新的方法,以你的接口,以保證Spring能夠代理他們
  • 移動到使用基於CGLIB代理(這些不依賴接口)
+0

沒有課是POJO,沒有接口實現 –

+0

哦。仍然。這一切都是真的:) –

+0

我並不反對我(只是說它不適用於這種情況:-) –