我目前正在开发一个基于桌面的中型管理和配置工具,使用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属性插入了一个新的用户行。
我的实际问题是: 这种做法“好”吗?如果是,我该怎么做这个“永远”的新持久化上下文?每次调用包含并合并?
我应该摆脱我的服务类并直接在我的UI控制器中实现持久性操作吗?
一旦User-UI控制器加载并在应用程序的整个生命周期内使用它,我可以执行null
吗?
完全不同的东西?
答案 0 :(得分:0)
我的理解是你的应用程序使用Guice Persist。
这个问题的答案取决于你的用例;但是,你绝对需要意识到一件事:
只要EntityManager
处于打开状态,其基础持久性上下文就会跟踪每个持久性实体的每一次更改。
这意味着如果您在应用程序期间保持实体经理处于打开状态,那么每当您致电时, User.setLogin()
,你刚才所做的改变已被视为持久性。现在,转到update
方法,在已管理的实体上调用persist
无效;但是,由于您是从@Transactional
方法调用它,因此Guice会在事务中包装调用,因此,一旦方法结束,所有更改都将刷新到数据库。
这意味着,如果您在应用中同时修改多个实体,然后在其中一个实体上调用AbstractService.update
,那么您实际上会将应用所做的所有更改保存到其中同时,即使AbstractService.update
没有明确地调用它们。
使用实体管理器每事务方法确实更安全。在事务之间,将没有打开的持久性上下文,因此所有实体都将分离,这将防止对它们的任何更新意外地被刷新到数据库。
但是,出于同样的原因,您的update
方法需要在要在数据库中更新的实体上调用em.merge
。 merge
基本上告诉实体经理'请将此实体放回持久化上下文中,并使其具有所提供实体具有的确切状态'。调用persist
使它看起来好像是一个新实体,并且确实会出现PK约束违规。