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

Jpa Guice Persist是否提供事务范围的或应用程序管理的EntityManager?,jpa,guice,entitymanager,guice-persist,Jpa,Guice,Entitymanager,Guice Persist,我们在项目中使用EntityManager 例如 但我们不清楚如何使用EntityManager的注入实例 这是什么类型的EntityManager?(参见示例:)在hood GUI下,Persist通过EntityManager Factory.createEntityManager()实例化它,所以我认为它是应用程序管理的实体管理器。但在本文中,他们描述了seision-per-transaction策略,这表明EntityManager是(伪)事务范围的 我们是否应该手动对其调用close

我们在项目中使用EntityManager

例如

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

  • 这是什么类型的EntityManager?(参见示例:)在hood GUI下,Persist通过
    EntityManager Factory.createEntityManager()
    实例化它,所以我认为它是应用程序管理的实体管理器。但在本文中,他们描述了seision-per-transaction策略,这表明EntityManager是(伪)事务范围的
  • 我们是否应该手动对其调用close()?还是Guice会处理
  • 什么是范围?仅单个事务(如在事务范围的实体管理器中)或只要我使用相同的注入实例
    EntityManager
    (如在应用程序管理的实体管理器中)

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

    jpapersistance服务

    public class JpaPersistanceService implements Provider<EntityManager> {
    
      private EntityManagerFactory factory;
    
      public JpaPersistanceService(EntityManagerFactory factory) {
        this.factory = factory;
      }
    
      @Override
      public EntityManager get() {
        return factory.createEntityManager();
      }
    }
    
    @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
    
    用法

    public class JpaPersistanceService implements Provider<EntityManager> {
    
      private EntityManagerFactory factory;
    
      public JpaPersistanceService(EntityManagerFactory factory) {
        this.factory = factory;
      }
    
      @Override
      public EntityManager get() {
        return factory.createEntityManager();
      }
    }
    
    @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
    
    @Inject@Named(“请求”)
    私人实体管理者//在每个请求中注入一个新的EntityManager类
    @注入@Named(“会话”)
    私人供应商emProvider//在每个会话中注入一个新的EntityManager类
    //这有点棘手,因为EntityManager存储在会话中。在阶段中,生产都是急切地创建的注入,并且在注入时没有会话。会话绑定应该以惰性的方式完成-注入提供程序并在需要em时调用emProvider.get();
    @注入@Named(“应用程序”)
    私人实体管理者//注入单态
    
    二,。是的,你应该或者你将使用


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

    我对Guice persist的源代码进行了一些研究,并在Guice persist wiki页面下通读了注释,以下是我需要的答案:

    一,。如果EntityManager是通过@InjectEntityManager注入的,那么它的生命周期管理就有点崩溃。如维基上的一条评论所述:

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

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

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

    2.如果注入和使用正确,则我不需要手动关闭实体管理器(guice persist将为我处理)。如果使用错误,手动关闭将是一个非常糟糕的主意(当我@Injection EntityManager时,EntityManager的关闭实例将被注入到每个位置)


    3.如果注入和使用正确,则一级缓存的作用域为单事务。如果使用错误,一级缓存的作用域为应用程序的生命周期(EntityManager为单事务)

    即使Piotr完美地回答了这个问题,我想补充一些关于如何使用guice persist的实用建议

    我的应用程序中某些线程会显示过时的数据,有时
    EntityManager
    实例会留下旧的死掉的数据库连接。根本原因在于我使用
    @Transactional
    注释的方式(我只将它们用于执行更新/插入/删除的方法,而不是只读方法)。只要在注入的
    提供程序
    实例上调用
    get()
    ,guice persist就会将
    EntityManager
    实例存储在
    ThreadLocal
    中(这是我为只读方法所做的)。但是,只有同时调用
    UnitOfWork.end()
    (如果方法上有
    @Transactional
    ,则通常由拦截器执行)时,才会删除此
    ThreadLocal
    。不这样做会将EM实例保留在ThreadLocal中,这样线程池中的每个线程最终都会有一个旧EM实例,其中包含过时的缓存实体

    因此,只要您坚持以下简单规则,guice persist的使用就一目了然:

  • 始终插入
    Provider
    ,而不是直接插入
    EntityManager
  • 对于事务范围语义:始终使用
    @Transactional
    (甚至是只读方法)注释每个方法。这样,
    JpaLocalTxnInterceptor
    将拦截对注释方法的调用,确保不仅启动和提交事务,而且在ThreadLocal中设置和取消设置EM实例
  • 对于请求范围语义:使用guice persist附带的
    PersistFilter
    servlet筛选器。在请求完成之前和之后,它将在
    UnitOfWork
    上为您调用
    begin()
    end()
    ,从而填充和清理ThreadLocal

  • 希望这有帮助!

    我正在注射一个提供者……但我怀疑出了什么问题。 当我尝试重新部署应用程序时,我总是必须重新启动服务器,因为JPA类是缓存的

    出现以下伪错误

    理论上是通过注入一个提供者并获取实体的实例