JPA merge перезаписывает загруженное свойство - PullRequest
0 голосов
/ 27 февраля 2019

Сначала позвольте мне сказать, что я новичок в 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?

1 Ответ

0 голосов
/ 27 февраля 2019

Вы также каскадно обновляете операцию, стирая все существующие изменения в дочернем объекте.Это может ожидаться приложением, но теперь, когда отношение Parent-> Child является ленивым, время обновления также является ленивым и выполняется при первом доступе к отношению.Любые изменения, сделанные до доступа selectedParent.getChild (), будут потеряны.До того, как вы сделали его ленивым, любые изменения в дочернем объекте до вызова обновления были бы потеряны.

Вы захотите оценить параметры каскада и убедиться, что они соответствуют тому, что вам действительно нужно из приложения.

...