首先,我要说我是JPA的新手。我的任务是维护一些我未编写的现有代码。我做了一个简单的更改,即将FetchType.LAZY
添加到现有的@OneToOne
注释中。这减少了在我们的应用程序中的一页上进行查询时进行的数据库调用次数。但是,这会产生意想不到的结果,即在对merge
进行EntityManager
时,现在在单独的页面上会丢失数据。在没有FetchType.LAZY
的情况下,合并工作正常。
以下是一些高度简化的代码来说明此问题。 JPA实体:
@Entity
@Table(name = "parent")
public class Parent {
@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Child child;
@Column(name = "name")
private String name;
// getters/setters omitted for brevity
}
@Entity
@Table(name = "child")
public class Child {
@OneToOne
@JoinColumn(name = "parent_id", nullable = false)
private Parent parent;
@Column(name = "name")
private String name;
// getters/setters omitted for brevity
}
请注意,我在FetchType.LAZY
属性的@OneToOne
注释中添加了child
(在最右边)。那以前不存在。
接下来,存在一个视图范围的Bean,它在Parent
方法中向数据库查询特定的postConstruct
。稍后,在通过ajax调用的方法中,刷新parent
,并重新分配child
的{{1}}。最后,稍后保存name
。
parent
我在ViewStack上问这个问题之前就发布了针对范围bean的代码的原因,我写了一个方法来查询@ManagedBean
@ViewScoped
public class DemoBean {
@Inject
private ParentDao parentDao;
private Parent selectedParent;
@PostConstruct
public void postConstruct() {
selectedParent = parentDao.findByName("Bob");
}
// Called via ajax sometime after postConstruct.
public void changeChildName() {
// Under the hood, `parentDao.refresh` calls `entityManager.refresh`.
selectedParent = parentDao.refresh(selectedParent);
selectedParent.getChild().setName("Joe");
}
// Called via ajax sometime after changeChildName.
public void save() {
// Under the hood, `parentDao.merge` calls `entityManager.merge`.
selectedParent = parentDao.merge(selectedParent);
}
}
,刷新Parent
,重新分配{{ 1}} parent
,然后保存child
。但是,如果我用一种方法全部解决,则无法重现该问题。因此,我猜测以某种方式在单独的时间分别调用这些操作是问题的一部分。
无论如何,问题在于,一旦我尝试name
Parent
,JPA就会将merge
级联到Parent
(这很不错),但是不好的部分似乎是要从数据库中“重新加载”子项并覆盖我的更改。合并后,对孩子名字的更改将丢失并且不会保存。如何在保留merge
的同时解决此问题?
答案 0 :(得分:1)
您也正在级联刷新操作,清除子对象中的所有现有更改。这可能是应用程序所期望的,但是既然Parent-> Child关系是惰性的,刷新的时间也是惰性的,并且在第一次访问该关系时就完成了。在selectedParent.getChild()访问之前所做的任何更改都将丢失。在变得懒惰之前,刷新调用之前对子对象的任何更改都将丢失。
您将需要评估您的级联选项,并确保它们符合您从应用程序中真正需要的选项。