2008-12-16 23 views
3

我正在尋找關於如何將運行時依賴注入到從Hibernate檢索的JPA實體中的建議。我的問題本質上是這樣的:需要模式建議(Hibernate + Guice)

我有一些交易對象的不同子類。每個Transaction子類在執行時都有不同的行爲,並且需要來自環境的一組不同的依賴關係。這些Transaction對象由Hibernate作爲JPA實體進行管理,因此我無法有效地使用Guice進行依賴注入,以便像我在其他應用程序中那樣使用它們的環境依賴項來填充實例。

要解決這個問題,我已經採取了一個辦法,就是有點類似訪問者模式,如下:

public abstract class Transaction { 
    // ...snip... 
    public abstract void apply(Transactor transactor); 
} 

public class TransactionA extends Transaction { 
    public void apply(Transactor transactor) { 
     transactor.execute(this); 
    } 
} 

public class TransactionB extends Transaction { 
    public void apply(Transactor transactor) { 
     transactor.execute(this); 
    } 
} 
// other Transaction subclasses with the same boilerplate 

public interface Transactor { 
    public void execute(TransactionA trans); 
    public void execute(TransactionB trans); 
    // corresponding methods for other transaction types. 
} 

public class BeginTransactor { 
    @Inject 
    private Foo execAdep; 
    public void execute(TransactionA trans) { 
     execAdep.doSomething(...)  
    } 

    @Inject 
    private Bar execBdep; 
    public void execute(TransactionB trans) { 
     execBdep.doOther(...)  
    } 
} 

我有一個交易週期的不同部分的交易者的各種實現方式。這些可以是依賴注入使用吉斯到我要處理的事務,在這裏我簡單的調用上下文:

Transactor transactor = injector.getInstance(BeginTransactor.class); //Guice injection 
Transaction t = ... //get a transaction instance 
t.apply(transactor); 

我不喜歡這種方法是什麼(1)不是每個類型的交易應該在每個生命週期階段都可執行,但每個Transactor必須爲每個事務子類實現一個execute()方法,並且(2)基本上沒有一個注入的依賴項用於處理多個事務類型。

本質上,我的Transactor接口&實現了很多不相干的crud glopped在一起。理想情況下,我只需在事務對象本身擁有execute()方法,但我不希望調用代碼必須知道事務的類型或它需要的依賴關係。另外,這可能會使測試變得更加困難,因爲如果它是Transaction對象的具體方法,我不能輕易地模擬出execute()方法。使用Transactor接口意味着我可以根據需要輕鬆地嘲笑它。

任何人都可以建議如何解決這個問題的類型安全的方式,不會導致一堆大多數不相關的行爲一起在Transactor中一起出現,但保持可測試性並允許依賴注入?

回答

5

我使用guice進行交易,但我使用AOP來執行它們。我幾乎沒有樣板,只是以一點「魔力」爲代價。只要你截取的課程是「在俱樂部裏」,它運作得非常好。

class BusinessLogic { 
    @Inject public EntityManager em; 

    @Transactional 
    publc void doSomething() { 
     //... 
     em.persist(myObj); 
    } 

    @Transactional 
    public void doSomethingElse() { 
     //... 
     em.delete(myObj); 
    } 
} 

class TransactionalInterceptor implements MethodInterceptor { 
    @Inject static Injector injector; 
    public Object intercept(MethodInvocation invocation) { 
     EntityManager em = injector.getInstance(EntityManager.class); 
     em.getTransaction().begin(); 
     Object result = invocation.proceed(); 
     em.getTransaction().commit(); 
     return result; 
    } 
} 
class TransactionalModule extends AbstractModule { 
    public void configure() { 
     requestStaticInjection(TransactionalInterceptor.class); 
     bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), 
       new TransactionalInterceptor()); 
    } 
} 
+0

非常好,謝謝!我曾懷疑可能有AOP方法解決這個問題,但並不確定如何去解決這個問題。 – 2008-12-17 16:19:04

2

檢查:http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Interceptor.html

配置Hibernate來使用Intercetor。該方法

public Object instantiate(String entityName, 
         EntityMode entityMode, 
         Serializable id) 

將被調用來

Instantiate the entity class. Return null to indicate that Hibernate 
should use the default constructor of the class. The identifier property 
of the returned instance should be initialized with the given identifier. 

你可以從那裏打電話給你injector.getInstance()。

也許,你可以使用getEntity()或onLoad()方法。從onLoad()方法你可以調用injector.injectMembers()。