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

Pio*_*zyk 17 jpa guice entitymanager guice-persist

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

例如

public class MyDao{
   @Inject
   EntityManager em;

   public void someMethod(){
       //uses em instance
   }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Dan*_*has 27

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

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

因此,只要您坚持以下简单规则,guice-persist的使用就是直截了当的:

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

希望这可以帮助!

  • 这是Guice-persist中一个巨大的设计缺陷!即使是一个小错误(在@Transactional之外使用EntityManager)也会破坏整个项目中的错误...例如; 由于挂起/过时的网络连接,我的Google App Engine项目在8小时后开始生成JDBC连接超时.原因(经过长时间的调试)是一个"pEntityManagerProvider.get()",在实用程序类中没有@Transactional ...... (3认同)

Pio*_*zyk 12

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

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是单例)


Mil*_*ran 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

模块绑定

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;
Run Code Online (Sandbox Code Playgroud)

用法

@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
Run Code Online (Sandbox Code Playgroud)

2.是的,您应该或者您将使用JpaPersistModule [javadoc]

嗯,这是关于persistence.xml和EntityManager范围中的JPA配置