Автоматическое сохранение дочерних объектов с помощью JPA Hibernate - PullRequest
59 голосов
/ 13 октября 2010

У меня есть отношение один-ко-многим между таблицей Parent и Child.В родительском объекте у меня есть

List<Child> setChildren(List<Child> childs)

У меня также есть внешний ключ в таблице Child.Этот внешний ключ является идентификатором, который ссылается на строку Родителя в базе данных.Так что в моей конфигурации базы данных этот внешний ключ не может быть NULL.Также этот внешний ключ является первичным ключом в родительской таблице.

Поэтому мой вопрос заключается в том, как я могу автоматически сохранять дочерние объекты, выполняя что-то вроде этого:

session.save(parent);

Я пробовал вышено я получаю ошибку базы данных с жалобой на то, что поле внешнего ключа в таблице Child не может быть NULL.Есть ли способ сообщить JPA, чтобы он автоматически устанавливал этот внешний ключ в объекте Child, чтобы он мог автоматически сохранять дочерние объекты?

Заранее спасибо.

Ответы [ 7 ]

63 голосов
/ 15 октября 2010

Я попробовал описанное выше, но получаю сообщение об ошибке базы данных, сообщающее, что поле внешнего ключа в таблице 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.setChildren(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);

Ссылки

24 голосов
/ 14 октября 2010

Я считаю, что вам нужно установить каскадную опцию в вашем отображении через xml / annotation См. справочный пример Hibernate здесь .

Если вы используете аннотацию, вам нужно сделать что-то вроде этого,

@OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..
2 голосов
/ 09 сентября 2016

Вот способы присвоения родительского объекта в дочернем объекте двунаправленных отношений?

Предположим, у вас есть отношение, скажем, One-To-Many, тогда для каждого родительского объекта,набор дочерних объектов существует.В двунаправленных отношениях каждый дочерний объект будет иметь ссылку на своего родителя.

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 для выполнения вашихсобственная работа перед выполнением любой операции с БД. Аналогично при сохранении объекта мы можем назначить родительский объект в дочерних объектах с помощью отражения.

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() );
1 голос
/ 03 марта 2017

Используйте org.hibernate.annotations для выполнения Cascade, если hibernate и JPA используются вместе, это как-то жалуется на сохранение дочерних объектов.

0 голосов
/ 20 марта 2018

Короче говоря, установите каскадный тип для всех, сделайте работу;Для примера в вашей модели.Добавьте код, как это.@OneToMany (mappedBy = "квитанция", cascade = CascadeType.ALL) приватный список saleSet;

0 голосов
/ 08 августа 2017

Следующая программа описывает, как двунаправленные отношения работают в спящем режиме.Когда родитель сохранит свой список, дочерний объект будет автоматически сохранен.

      @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();    
0 голосов
/ 13 октября 2010

в ваших setChilds, вы можете попробовать пройтись по списку и сделать что-то вроде

child.parent = this;

Вы также должны настроить каскад на родительском элементе на соответствующие значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...