3

我正在寻找有关如何将运行时依赖项注入从 Hibernate 检索的 JPA 实体的建议。我的问题本质上是这样的:

我有许多不同的 Transaction 对象子类。每个 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 实现。可以使用 Guice 将这些依赖注入到我想要处理事务的上下文中,我只需调用:

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

我不喜欢这种方法的是(1)并非每种类型的事务都应该在每个生命周期阶段都可以执行,但是每个 Transactor 都必须为每个事务子类实现一个 execute() 方法以及(2)基本上没有注入的依赖项用于处理一种以上的交易类型。

本质上,我的 Transactor 接口和实现有很多不相关的 crud 放在一起。理想情况下,我只在事务对象本身上有 execute() 方法,但我不希望调用代码必须知道事务的类型或它所需的依赖项。此外,这可能会使测试变得更加困难,因为如果 execute() 方法是 Transaction 对象上的具体方法,我将无法轻松模拟它。使用 Transactor 接口意味着我可以根据需要轻松地对其进行模拟。

任何人都可以建议如何以类型安全的方式解决这个问题,这种方式不会导致一堆大部分不相关的行为在 Transactor 中聚集在一起,而是保持可测试性并允许依赖注入?

4

2 回答 2

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());
    }
}
于 2008-12-17T01:18:32.913 回答
2

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

将您的休眠配置为使用拦截器。方法

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()。

于 2008-12-17T12:54:32.243 回答