Сохранять дочерние объекты автоматически с помощью JPA Hibernate
У меня есть отношение "один ко многим" между родительской и дочерней таблицей. В родительском объекте у меня есть
List<Child> setChilds(List<Child> childs)
У меня также есть внешний ключ в таблице Child. Этот внешний ключ является идентификатором, который ссылается на родительскую строку в базе данных. Поэтому в моей конфигурации базы данных этот внешний ключ не может быть NULL.
Также этот внешний ключ является первичным ключом в родительской таблице.
Итак, мой вопрос заключается в том, как я могу автоматически сохранять дочерние объекты, делая что-то вроде этого:
session.save(parent);
Я пробовал выше, но я получаю ошибку базы данных, жалуясь, что поле внешнего ключа в таблице Child не может быть NULL. Есть ли способ сообщить JPA автоматически установить этот внешний ключ в объект Child, чтобы он мог автоматически сохранять дочерние объекты?
Спасибо заранее.
Ответы
Ответ 1
Я пробовал выше, но я получаю ошибку базы данных, жалуясь, что поле внешнего ключа в таблице Child не может быть NULL. Есть ли способ сообщить JPA автоматически установить этот внешний ключ в объект Child, чтобы он мог автоматически сохранять дочерние объекты?
Ну, здесь есть две вещи.
Во-первых, вам нужно каскадировать операцию сохранения (но я понимаю, что вы это делаете или вы не получите нарушение ограничения FK во время вставок в "дочерней" таблице)
Во-вторых, у вас, вероятно, есть двунаправленная связь, и я думаю, что вы не устанавливаете "обе стороны ссылки" правильно. Вы должны сделать что-то вроде этого:
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChilds(children);
session.save(parent);
Общим примером является использование методов управления ссылками:
@Entity
public class Parent {
@Id private Long id;
@OneToMany(mappedBy="parent")
private List<Child> children = new ArrayList<Child>();
...
protected void setChildren(List<Child> children) {
this.children = children;
}
public void addToChildren(Child child) {
child.setParent(this);
this.children.add(child);
}
}
И код будет выглядеть следующим образом:
Parent parent = new Parent();
...
Child c1 = new Child();
...
parent.addToChild(c1);
session.save(parent);
Ссылки
- Справочное руководство по Hibernate Core
Ответ 2
Я считаю, что вам нужно установить параметр каскада в вашем сопоставлении через xml/annotation. Обратитесь к Пример ссылки Hibernate здесь.
Если вы используете аннотацию, вам нужно сделать что-то вроде этого,
@OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..
Ответ 3
Вот способы назначения родительского объекта в дочернем объекте двунаправленных отношений <
Предположим, что у вас есть отношение "Один-ко-многим", то для каждого родительского объекта существует набор дочерних объектов.
В двунаправленных отношениях каждый дочерний объект будет иметь ссылку на своего родителя.
eg : Each Department will have list of Employees and each Employee is part of some department.This is called Bi directional relations.
Чтобы достичь этого, одним способом следует назначить родительский объект в дочернем объекте, сохраняя родительский объект
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChilds(children);
session.save(parent);
Другой способ: вы можете использовать hibernate Intercepter, таким образом, вы не сможете писать код выше для всех моделей.
Перехватчик Hibernate предоставляет apis для выполнения вашей собственной работы перед выполнением любой операции с БД. Подобно onSave объекта мы можем назначить родительский объект в дочерних объектах с использованием отражения.
public class CustomEntityInterceptor extends EmptyInterceptor {
@Override
public boolean onSave(
final Object entity, final Serializable id, final Object[] state, final String[] propertyNames,
final Type[] types) {
if (types != null) {
for (int i = 0; i < types.length; i++) {
if (types[i].isCollectionType()) {
String propertyName = propertyNames[i];
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
try {
Method method = entity.getClass().getMethod("get" + propertyName);
List<Object> objectList = (List<Object>) method.invoke(entity);
if (objectList != null) {
for (Object object : objectList) {
String entityName = entity.getClass().getSimpleName();
Method eachMethod = object.getClass().getMethod("set" + entityName, entity.getClass());
eachMethod.invoke(object, entity);
}
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return true;
}
}
И вы можете зарегистрировать Intercepter для конфигурации как
new Configuration().setInterceptor( new CustomEntityInterceptor() );
Ответ 4
Используйте org.hibernate.annotations
для выполнения Cascade
, если используются hibernate
и JPA
, его как-то жалуются на сохранение дочерних объектов.
Ответ 5
в ваших setChilds, вы можете попробовать выполнить цикл через список и сделать что-то вроде
child.parent = this;
вы также должны настроить каскад родителя на соответствующие значения.
Ответ 6
В следующей программе описывается, как двунаправленное отношение работает в спящем режиме. Когда родитель сохранит свой список дочерних объектов, будет автоматически сохранено.
@Entity
@Table(name="clients")
public class Clients implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@OneToMany(mappedBy="clients", cascade=CascadeType.ALL)
List<SmsNumbers> smsNumbers;
on the parent side
and put the following annotation on the child side
@Entity
@Table(name="smsnumbers")
public class SmsNumbers implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
String number;
String status;
Date reg_date;
@ManyToOne
@JoinColumn(name = "client_id")
private Clients clients;
and getter setter.
public static void main(String arr[])
{
Session session = HibernateUtil.openSession();
//getting transaction object from session object
session.beginTransaction();
Clients cl=new Clients("Murali", "1010101010");
SmsNumbers sms1=new SmsNumbers("99999", "Active", cl);
SmsNumbers sms2=new SmsNumbers("88888", "InActive", cl);
SmsNumbers sms3=new SmsNumbers("77777", "Active", cl);
List<SmsNumbers> lstSmsNumbers=new ArrayList<SmsNumbers>();
lstSmsNumbers.add(sms1);
lstSmsNumbers.add(sms2);
lstSmsNumbers.add(sms3);
cl.setSmsNumbers(lstSmsNumbers);
session.saveOrUpdate(cl);
session.getTransaction().commit();
session.close();