gpt4 book ai didi

java - 如何使用 "long living entities"或 "longer living persistence context"?

转载 作者:太空宇宙 更新时间:2023-11-04 11:28:21 26 4
gpt4 key购买 nike

我目前正在开发一个用 Java 实现的中型、基于桌面的管理和配置工具,使用 JavaFx、google-guice 和 hibernate 来实现其 jpa。

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

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

我可以尝试让我当前的单例方法使用类似的东西:

@Inject 
@PersistenceContext(name="JPA-Unit1")
@Singleton
private EntityManager em;

它从来没有感觉完美,但感觉“丑陋”。由于我在使用 guice 获取多个持久性上下文时遇到了严重的问题,因此我必须对这个主题进行大量研究。

我遇到了几个博客 SO 问题,要么提到 EntityManager 的实例应该只在需要时存在,要么提到一些扩展的持久性上下文。

由于我使用JavaFx,所以我使用*Property将数据直接绑定(bind)到 UI 的类。

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

    @Entity
@Table(name = "USERS")
@NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements Serializable {

[...]

private final SimpleStringProperty loginProperty = new SimpleStringProperty();

public User() {
}

public String getLogin() {
return this.loginProperty.get();
}

public void setLogin(String login) {
this.loginProperty.set(login);
}

public SimpleStringProperty loginProperty() {
return this.loginProperty;
}

[...]
}

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

this.login.textProperty().bindBidirectional(user.loginProperty());

不需要大量的“业务逻辑”。它全部通过(输入)验证来处理。如果所有输入都有效,我只需通过保存数据

userService.update(user);

UserService 的部分(确切地说:它的抽象父类(super class)):

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;
}
}

如您所见:服务类非常简单。我什至可以删除所有这些“服务”类并直接在我的 UI Controller 中使用实体管理器。

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

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

  • 我应该摆脱我的服务类并直接在我的 UI Controller 中实现持久性操作吗?

  • 我可以在用户 UI Controller 加载后执行 this.emProvider.get() 并在应用程序的整个生命周期中使用它吗?

  • 完全不同的东西?

  • 最佳答案

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

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

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

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

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

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

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

    关于java - 如何使用 "long living entities"或 "longer living persistence context"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44101043/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com