19

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

例如

public class MyDao{
   @Inject
   EntityManager em;

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

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

  1. 这是什么类型的EntityManager?(参见例如:实体管理器的类型) Guice Persist 通过实例化它,EntityManagerFactory.createEntityManager()所以我说它是应用程序管理的实体管理器。但是在官方 Wiki中,他们写了关于每次事务的 Seesion策略,这表明 EntityManager 是(伪)事务范围的。
  2. 我们应该手动调用 close()吗?还是 Guice 会处理它?
  3. 一级缓存的范围是什么?只有单个事务(如在事务范围的实体管理器中)还是只要我使用相同的注入实例EntityManager(如在应用程序管理的实体管理器中)?
4

4 回答 4

30

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

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

因此,只要您遵守以下简单规则,guice-persist 的使用就很简单:

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

希望这可以帮助!

于 2014-02-10T14:30:22.173 回答
12

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

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

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

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

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

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

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

于 2013-08-12T20:59:56.243 回答
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配置

于 2013-08-12T11:26:49.130 回答
0

我正在注入提供程序....但我怀疑有问题。当我尝试重新部署应用程序时,我必须重新启动服务器,因为 JPA 类已缓存。

它发生以下伪错误

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

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

于 2015-02-18T14:52:44.787 回答