Java 如何与“合作”;“长寿实体”;或;长寿的持久性上下文“;?

Java 如何与“合作”;“长寿实体”;或;长寿的持久性上下文“;?,java,jpa,Java,Jpa,我目前正在开发一个中型的、基于桌面的管理和配置工具,该工具使用JavaFx、GoogleGuice和hibernate实现jpa,并用Java实现 到目前为止,我只使用了一个EntityManager作为@Singleton注入。这意味着我从启动到关闭整个EntityManager都处于“打开”状态。所有加载的实体在上下文中都是永久已知的,我对这种方法几乎没有任何问题。虽然我知道/相信这不是最好的解决方案(但很简单,我没有时间重新设计应用程序) 现在,应用程序得到扩展,我必须同时使用多个持久性单

我目前正在开发一个中型的、基于桌面的管理和配置工具,该工具使用JavaFx、GoogleGuice和hibernate实现jpa,并用Java实现

到目前为止,我只使用了一个
EntityManager
作为
@Singleton
注入。这意味着我从启动到关闭整个EntityManager都处于“打开”状态。所有加载的实体在上下文中都是永久已知的,我对这种方法几乎没有任何问题。虽然我知道/相信这不是最好的解决方案(但很简单,我没有时间重新设计应用程序)

现在,应用程序得到扩展,我必须同时使用多个持久性单元

我可以尝试使用以下方式使我当前的单例方法工作:

@Inject 
@PersistenceContext(name="JPA-Unit1")
@Singleton
private EntityManager em;
它从未感觉完美,但感觉“丑陋”。由于我在使用guice获取多个持久性上下文时遇到了严重的问题,所以我不得不对这个主题进行大量研究

我在几篇博客上看到过这样的问题,要么提到EntityManager的实例应该只在需要的时候存在,要么提到一些扩展的持久性上下文

因为我使用javafx,所以我使用类将数据直接绑定到UI中

简化用户实体(基于属性的访问):

如果我开始在UI中编辑用户数据,它将在实体中直接更新:

this.login.textProperty().bindBidirectional(user.loginProperty());
不需要广泛的“业务逻辑”。它通过(输入)验证得到所有处理。如果所有输入有效,我只需通过

userService.update(user);
UserService的部分(确切地说:它的抽象超类):

公共抽象类AbstractService实现GenericService{
保护类clazz;
@PersistenceContext(name=“JPA-Unit1”)
@注入
受保护的提供者提供者;
公共服务(课堂){
this.clazz=clazz;
}
@交易的
@凌驾
公共类型创建(类型实体){
this.emProvider.get().persist(实体);
返回实体;
}
@交易的
@凌驾
公共类型更新(类型实体){
this.emProvider.get().persist(实体);
返回实体;
}
}
如您所见:服务类非常简单。我甚至可以删除所有这些“服务”类,直接在UI控制器中使用entitymanager

在该服务中,您可以看到我编辑的用户先前通过其命名查询加载并放入列表的“问题”。加载也通过
@Transactional
方法完成。 但是每次我调用
this.emProvider.get()
时,我都会得到一个新实例,其上下文为空。如果我想保存以前编辑过的用户,我有一个问题,那就是persist实际上执行了一个insert(我假设它在上下文[detached]中未知),这会导致PK约束冲突,或者如果我删除(
null
)它的ID属性,就会插入一个新的用户行

我的实际问题是: 1.这种方法“行”吗?如果是,我该如何处理这个“始终”的新持久性上下文?每次调用都包含和合并吗

  • 我应该放弃我的服务类,直接在UI控制器中实现持久性操作吗

  • 加载用户UI控制器后,我是否可以执行
    this.emProvider.get()
    ,并在应用程序的整个生命周期内使用它

  • 完全不同的东西


  • 我的理解是你的应用程序使用Guice Persist

    这个问题的答案取决于您的用例;然而,你绝对需要意识到一件事:

    只要一个
    EntityManager
    处于打开状态,它的底层持久性上下文就会跟踪每个持久性实体的每一个更改

    这意味着,如果您在应用程序期间保持实体管理器处于打开状态,则无论何时调用(例如,
    User.setLogin()
    ),您刚才所做的更改都已被视为持久更改。现在,转到您的
    update
    方法,对已被管理的实体调用
    persist
    ,没有任何效果;但是,由于您是从
    @Transactional
    方法调用它,Guice将调用包装在事务中,因此,一旦方法结束,所有更改都将刷新到数据库中

    这意味着,如果您在应用程序中一次修改多个实体,然后对其中一个实体调用
    AbstractService.update
    ,则您实际上将同时保存应用程序对其他实体所做的所有更改,即使未对其显式调用
    AbstractService.update

    使用每个事务的实体管理器方法确实安全得多。在事务之间,将没有开放的持久性上下文,因此所有实体都将分离,这将防止它们上的任何更新意外刷新到数据库中


    但是,出于同样的原因,您的
    update
    方法将需要对要在数据库中更新的实体调用
    em.merge
    merge
    基本上是告诉实体管理器“请将此实体放回持久性上下文,并使其具有所提供实体的确切状态”。调用
    persist
    会使它看起来像是一个新实体,PK约束冲突也会随之发生

    我的理解是,您的应用程序使用Guice Persist

    这个问题的答案取决于您的用例;然而,你绝对需要意识到一件事:

    只要一个
    EntityManager
    处于打开状态,它的底层持久性上下文就会跟踪每个持久性实体的每一个更改

    这意味着,如果在应用程序期间保持实体管理器处于打开状态,则无论何时调用(例如,
    User.setLogin()
    )时
    userService.update(user);
    
    public abstract class AbstractService<PK extends Serializable, Type> implements GenericService<PK, Type> {
    
    protected Class<Type> clazz;
    
    @PersistenceContext(name = "JPA-Unit1")
    @Inject
    protected Provider<EntityManager> emProvider;
        public AbstractService(Class<Type> clazz) {
            this.clazz = clazz;
        }
    
        @Transactional
        @Override
        public Type create(Type entity) {
            this.emProvider.get().persist(entity);
            return entity;
        }
    
        @Transactional
        @Override
        public Type update(Type entity) {
            this.emProvider.get().persist(entity);
            return entity;
        }
    }