JPA: одновременная инициализация обоих объектов в отношениях родитель/потомок
Я новичок в JPA и не могу найти способ правильно инициализировать набор дочерних объектов при сохранении родительского объекта с использованием собственного репозитория.
У меня есть родительский объект, который содержит список дочерних элементов, экземпляр которых я хочу создать при создании родительского объекта, например:
@Entity(name = "parent")
@Table(schema = "XXX", name = "PARENT")
public class Parent {
@Id
@GeneratedValue
@Column(name = "ID_PARENT", nullable = false)
private int idParent;
@OneToMany(targetEntity = Child.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "ID_PARENT", referencedColumnName = "ID_PARENT")
private List<Child> childList= new ArrayList<>();
// Constructor + getters/setters...
Дочерний класс (ключ таблицы состоит из родительского идентификатора + собственного идентификатора, не спрашивайте почему):
@Entity(name = "child")
@Table(schema = "XXX", name = "CHILD")
@IdClass(ChildPrimaryKey.class)
public class Child{
@Id
@Column(name = "ID_CHILD", nullable = false)
private int idChild;
@Id
@Column(name = "ID_PARENT", nullable = false)
private int idParent;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Parent.class)
@JoinColumn(name = "ID_PARENT", nullable = false, insertable = false, updatable = false)
private Parent parent;
// Constructor + getters/setters...
И, наконец, класс ChildPrimaryKey:
public class ChildPrimaryKey implements Serializable {
private static final long serialVersionUID = XXXL;
private int idChild;
private int idParent;
// Constructors + getters/setters
Проблема, с которой я столкнулся, заключается в том, что между полем ParentId класса Child и его родительским полем нет прямой связи. Поэтому, когда я сохраняю родительский объект (со списком дочерних элементов, инициализированных внутри поля childList), используя его репозиторий (базовый CrudRepository с некоторыми пользовательскими запросами), тогда родительский объект правильно сохраняется, а его идентификатор получает сгенерированное значение, как и хотелось. Но затем дочерние объекты сохраняются, но без правильного идентификатора, сгенерированного для родительского объекта (по умолчанию 0, поскольку, я думаю, это целое число?), что приводит к исключению нарушения ограничения, поскольку столбец ID_PARENT должен ссылаться на фактический родительский идентификатор.
Я пробовал использовать @PrimaryKeyJoinColumn следующим образом:
@Entity(name = "child")
@Table(schema = "XXX", name = "CHILD")
@IdClass(ChildPrimaryKey.class)
public class Child{
@Id
@Column(name = "ID_CHILD", nullable = false)
private int idChild;
@Id
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Parent.class)
@PrimaryKeyJoinColumn(name = "ID_PARENT", referencedColumnName = "ID_PARENT")
private Parent parent;
// Constructor + getters/setters...
В этом случае, поскольку поле idParent не существует в классе Child, я получаю ошибку компиляции, поскольку класс ChildPrimaryKey ожидает присутствия этого поля, плюс оно мне нужно для запросов к хранилищу.(Я также пытался добавить поле int, как и раньше, но оно все равно не получило никакого значения)
Может ли кто-нибудь объяснить мне, что я делаю неправильно? Как я могу правильно передать сгенерированный идентификатор родителя при создании дочерних элементов?
Я хотел бы избежать необходимости сначала создавать родительский элемент, а затем получать его идентификатор, чтобы затем создавать дочерние элементы индивидуально, давая им правильный идентификатор.
1 ответ
Используйте эту модель:
@Entity(name = "parent")
@Table(schema = "XXX", name = "PARENT")
public class Parent {
@Id
@GeneratedValue
@Column(name = "ID_PARENT", nullable = false)
private int idParent;
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
}
@Entity(name = "child")
@Table(schema = "XXX", name = "CHILD")
@IdClass(ChildPrimaryKey.class)
public class Child {
@Id
@Column(name = "ID_CHILD", nullable = false)
private int idChild;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ID_PARENT")
private Parent parent;
}
public class ChildPrimaryKey implements Serializable {
private static final long serialVersionUID = XXXL;
private int idChild;
private int parent;
}