EJB3 и сохранение иерархии вручную

У меня есть устаревшая база данных, для моделирования которой я использую EJB3. База данных находится в довольно плохом состоянии, и у нас есть некоторые необычные ограничения на то, как мы вставляем в базу данных. Теперь я хочу смоделировать базу данных в иерархии, которая соответствует структуре БД, но я хочу иметь возможность вручную вставлять каждую сущность по отдельности без того, чтобы диспетчер сохраняемости пытался сохранить дочерние сущности.

Я пробую что-то вроде следующего (шаблон не указан):

@Entity
@Table(name = "PARENT_TABLE")
public class Parent {
    @Id
    @Column(name = "ID")
    int id;

    @OneToMany
    List<Child> children;
}


@Entity
@Table(name = "CHILD_TABLE")
public class Child {
    @Id
    @Column(name = "ID")
    int id;   
}

Теперь это вызывает исключение:

java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST

Теперь я знаю, что объект не помечен как НАСТОЯЩИЙ - я не хочу, чтобы EntityManager сохранял его! Я хочу иметь возможность сохранить сначала родителя, а затем ребенка - но не вместе. Для этого есть веские причины, но, похоже, он не хочет играть.

Ответов (1)

Решение

Хех, добро пожаловать в конфигурацию JPA.

В вашем случае у вас есть два варианта:

  1. Сохраните новый объект вручную; или
  2. Автоматически сохранять это.

Для автоматического сохранения вам необходимо аннотировать отношения. Это распространенная идиома "один ко многим":

@Entity
@Table(name = "PARENT_TABLE")
public class Parent {
  @Id private Long id;

  @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
  private Collection<Child> children;

  public void addChild(Child child) {
    if (children == null) {
      children = new ArrayList<Child>();
    }
    child.setParent(parent);
    children.add(child);
  }
}

@Entity
@Table(name = "CHILD_TABLE")
public class Child {
  @Id private Long id;

  @ManyToOne
  private Parent parent;

  public void setParent(Parnet parent) {
    this.parent = parent;
  }
}

Parent parent = // build or load parent
Child child = // build child
parent.addChild(child);

Из-за сохранения каскада это будет работать.

Примечание. Вы должны сами управлять отношениями на уровне Java, поэтому вручную установите родительский элемент. Это важно.

Без него вам нужно вручную сохранить объект. Для этого вам понадобится EntityManager, и в этом случае это так же просто, как:

entityManager.persist(child);

В этот момент он будет работать правильно (при условии, что все остальное работает).

Для чисто дочерних сущностей я бы предпочел подход аннотации. Так проще.

Есть одна проблема, которую я упомяну с JPA:

Parent parent = new Parent();
entityManager.persist(parent);
Child child = new Child();
parent.addChild(child);

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