Guice Persist是否提供事务范围或应用程序管理的EntityManager?

时间:2013-08-07 10:51:22

标签: jpa guice entitymanager guice-persist

我们使用Guice Persist在我们的项目中注入EntityManager。

E.g。

public class MyDao{
   @Inject
   EntityManager em;

   public void someMethod(){
       //uses em instance
   }
}

但我们还不清楚如何使用EntityManager的注入实例。

  1. 这是什么类型的EntityManager?(参见例如:types of entity managers)引擎盖Guice Persist通过EntityManagerFactory.createEntityManager()实例化它,所以我说它是应用程序管理的实体经理。但是在official Wiki中,他们写了关于每个事务的视频策略,这表明EntityManager是(伪)事务范围的。
  2. 我们应该手动调用close()吗?或者Guice会照顾它吗?
  3. first level cache的范围是什么?只有单个事务(比如在事务范围的实体管理器中)或只要我使用EntityManager的相同注入实例(比如在应用程序管理实体管理器中)?

4 个答案:

答案 0 :(得分:26)

尽管Piotr完全回答了这个问题,但我想就如何使用guice-persist添加一些实用的建议。

我一直遇到很难调试的问题。在我的应用程序中,某些线程会显示过时的数据,有时EntityManager个实例会留下旧的死数据库连接。根本原因在于我使用@Transactional注释的方式(我只将它们用于执行更新/插入/删除的方法,而不是用于只读方法)。只要在注入的EntityManager实例(我为只读方法执行)上调用ThreadLocal,guice-persist就会在get()中存储Provider<EntityManager>个实例。但是,只有在您调用ThreadLocal时才会删除此UnitOfWork.end()(如果@Transactional在方法上,通常由拦截器完成)。不这样做会将EM实例留在ThreadLocal中,这样最终线程池中的每个线程都会有一个旧的EM实例和过时的缓存实体。

所以,只要您遵守以下简单规则,guice-persist的使用就是直截了当的:

  1. 始终直接注入Provider<EntityManager>而不是EntityManager
  2. 对于事务范围的语义:始终使用@Transactional(即使是只读方法)注释每个方法。这样JpaLocalTxnInterceptor将拦截对带注释方法的调用,不仅要确保启动和提交事务,还要确保在ThreadLocal中设置和取消设置EM实例。
  3. 对于请求范围的语义:使用guice-persist附带的PersistFilter servlet过滤器。在请求完成之前和之后,它会在begin()上为您调用end()UnitOfWork,从而填充并清理ThreadLocal。
  4. 希望这有帮助!

答案 1 :(得分:11)

我对Guice-persist的源代码进行了一些研究,并在Guice-persist维基页面下阅读了评论,这些是我需要的答案:

1。如果通过@Inject EntityManager注入,EntityManager的生命周期管理就会破碎。正如Wiki上的一条评论所述:

  

我确认直接注入EntityManager而不是提供者   可能很危险。如果您不在UnitOfWork或方法中   使用@Transaction注释,第一次注入EntityManager   在一个线程中将创建一个新的EntityManager,永远不会销毁它,并且   始终对此线程使用此特定的EntityManager(存储EM)   本地线程)。这可能导致可怕的问题,如注射   死的entityManager(连接关闭等)所以我的建议如果   总是注入一个提供者,或至少注入   直接在已打开的UnitOfWork内部的EntityManager。

所以在我的问题中的例子不是最正确的用法。它创建EntityManager的单例实例(每个线程)并将在任何地方注入此实例: - (。

但是,如果我注入了Provider并在@Transactional方法中使用它,那么EntityManager的实例将是每个事务。所以这个问题的答案是:如果正确注入和使用,实体管理器是事务范围的

2。 如果注入并正确使用,那么我不需要手动关闭实体管理器(guice-persist将为我处理)。如果使用不正确,手动关闭将是非常糟糕的想法(当我@Inject EntityManager时,EntityManager的闭合实例将被注入每个地方)

3。 如果正确注入和使用,则L1缓存的范围是单个事务。如果使用不正确,L1缓存的范围是应用程序的生命周期(EntityManager是单例)

答案 2 :(得分:6)

1。这取决于您的模块配置。有一些基本的绑定:

<强> JpaPersistanceService

public class JpaPersistanceService implements Provider<EntityManager> {

  private EntityManagerFactory factory;

  public JpaPersistanceService(EntityManagerFactory factory) {
    this.factory = factory;
  }

  @Override
  public EntityManager get() {
    return factory.createEntityManager();
  }
}

模块绑定

EntityManagerFactory factory = Persistence.createEntityManagerFactory(getEnvironment(stage));
bind(EntityManager.class).annotatedWith(Names.named("request")).toProvider(new JpaPersistanceService(factory)).in(RequestScoped.class);
bind(EntityManager.class).annotatedWith(Names.named("session")).toProvider(new JpaPersistanceService(factory)).in(SessionScoped.class);
bind(EntityManager.class).annotatedWith(Names.named("app")).toProvider(new JpaPersistanceService(factory)).asEagerSingleton;

<强>用法

@Inject @Named("request")
private EntityManager em; //inject a new EntityManager class every request

@Inject @Named("session")
private Provider<EntityManager> emProvider; //inject a new EntityManager class each session
//This is little bit tricky, cuz EntityManager is stored in session. In Stage.PRODUCTION are all injection created eagerly and there is no session at injection time. Session binding should be done in lazy way - inject provider and call emProvider.get() when em is needed;

@Inject @Named("application")
private EntityManager em; //inject singleton

2。是的,您应该使用JpaPersistModule [javadoc]

3。好吧,这是关于persistence.xml和EntityManager范围

中的JPA配置

答案 3 :(得分:0)

我正在注射供应商....但我怀疑有些事情是错误的。 当我尝试重新部署应用程序时,我必须重新安装服务器,因为JPA类是缓存的。

发生以下伪bug

https://bugs.eclipse.org/bugs/show_bug.cgi?id=326552

理论上,通过注入Provider并获取EntityManager的实例,你不应该关闭任何东西....