Сначала позвольте мне сказать, что я новичок в 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
(посмотрите в крайнее правое положение).Этого раньше не было.
Далее, существует компонент в области видимости, который запрашивает у базы данных конкретный Parent
в методе postConstruct
.Позже, в методе, вызванном через ajax, parent
обновляется и child
name
переназначается.Наконец, в какой-то момент позже parent
сохраняется.
@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);
}
}
Причина, по которой я публикую код для bean-объекта в области видимости, заключается в том, что перед тем, как задавать этот вопрос в StackOverflow, я написал один метод, который запрашиваетдля Parent
обновляет parent
, переназначает child
name
, а затем сохраняет Parent
.Тем не менее, я не смог бы воспроизвести проблему, если бы я сделал все это одним способом.Поэтому я предполагаю, что каким-то образом наличие этих операций в отдельных методах, вызываемых в разное время, является частью проблемы.
В любом случае, проблема в том, что, как только я пытаюсь merge
Parent
, JPAкаскадные merge
к child
(что хорошо), но плохая часть в том, что он «перезагружает» ребенка из базы данных и перезаписывает мои изменения.После слияния изменение имени ребенка теряется и не сохраняется.Как я могу это исправить, сохраняя FetchType.LAZY
?