如何使用“长寿实体”或“更长寿的持久性背景”?

时间:2017-05-21 19:39:47

标签: java jpa

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

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

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

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

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

它从未感觉完美,但感觉“丑陋”。由于我在使用guice进行多个持久化上下文时遇到了严重问题,因此我不得不对此主题进行大量研究。

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

由于我使用了JavaFx,因此我使用*Property类将数据直接绑定到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的一部分(确切地说:它的抽象超类):

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控制器中直接使用entitymanager。

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

我的实际问题是: 这种做法“好”吗?如果是,我该怎么做这个“永远”的新持久化上下文?每次调用包含并合并?

  1. 我应该摆脱我的服务类并直接在我的UI控制器中实现持久性操作吗?

  2. 一旦User-UI控制器加载并在应用程序的整个生命周期内使用它,我可以执行null吗?

  3. 完全不同的东西?

1 个答案:

答案 0 :(得分:0)

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

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

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

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

这意味着,如果您在应用中同时修改多个实体,然后在其中一个实体上调用AbstractService.update,那么您实际上会将应用所做的所有更改保存到其中同时,即使AbstractService.update没有明确地调用它们

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

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