2008-12-16 28 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()。