JPA Hibernate를 사용하여 자동으로 자식 개체 저장


80

부모 테이블과 자식 테이블간에 일대 다 관계가 있습니다. 부모 개체에는

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

또한 Child 테이블에 외래 키가 있습니다. 이 외래 키는 데이터베이스의 상위 행을 참조하는 ID입니다. 따라서 내 데이터베이스 구성 에서이 외래 키는 NULL이 될 수 없습니다. 또한이 외래 키는 Parent 테이블의 기본 키입니다.

그래서 내 질문은 다음과 같은 작업을 수행하여 자식 개체를 자동으로 저장하는 방법입니다.

session.save(parent);

위의 작업을 시도했지만 Child 테이블의 외래 키 필드가 NULL이 될 수 없다는 내용의 데이터베이스 오류가 발생합니다. JPA에이 외래 키를 자동으로 자식 개체에 설정하도록 지시하여 자식 개체를 자동으로 저장할 수 있습니까?

미리 감사드립니다.


매핑, 코드 및 원래 오류를 보여주십시오.
Adeel Ansari

답변:


90

위의 작업을 시도했지만 Child 테이블의 외래 키 필드가 NULL 일 수 없다는 내용의 데이터베이스 오류가 발생합니다. JPA에이 외래 키를 자동으로 자식 개체에 설정하도록 지시하여 자식 개체를 자동으로 저장할 수 있습니까?

여기에 두 가지가 있습니다.

첫째, 저장 작업을 계단식으로 작성해야합니다 (하지만 내 이해는이 작업을 수행하고 있거나 "하위"테이블에 삽입하는 동안 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.addToChildren(c1);

session.save(parent);
참고 문헌

1
그래 내 문제는 실제로 단방향 매핑을 원했지만 대신 양방향 매핑과 같은 것이 있다는 것입니다. 문제는 내가 Child 테이블에 외래 키를 매핑했다는 것입니다. 그래서 나는 그것을 Child 테이블에서 제거하고 작동했습니다. 부모 테이블에서 @OneToMany와 @JoinColumn을 사용했습니다. 감사.
Marquinio

@pascal 좋은 설명! 파스칼 감사합니다. 부모 측에서 설정하는 동안 위의 작업을 수행해야 함을 이해합니다. 하지만 자식 측에서 설정하면 다음과 같은 충분한 child.setParent (parent)?
HopeKing 2017 년

34

xml / annotation을 통해 매핑에서 캐스케이드 옵션을 설정해야한다고 생각합니다. 여기 에서 Hibernate 참조 예제를 참조하십시오 .

주석을 사용하는 경우 다음과 같이해야합니다.

@OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..

사실, 기본 캐스케이드 동작은 아무것도 아닙니다.
masterziv

10
나는 그것이 있어야한다고 생각한다 @OneToMany(cascade = CascadeType.ALL), 삽입이 존재하지 않는다!
tommueller 2013

newnoise : 나는 요즘 Hibernate와 접촉하지 않는다. 그러나 그 당시에는 사용 가능한 옵션 중 하나였습니다.
Adeel Ansari 2013

2
CascadeType.INSERT로 대체됩니다 CascadeType.PERSIST.
Adeel Ansari 2013-04-17

나는 CascadeType.PERSIST. 작동하지 않았습니다. 교체 CascadeType.ALL하면 문제가 해결되었습니다.
Jelle den Burger

2

다음 프로그램은 최대 절전 모드에서 양방향 관계가 작동하는 방식을 설명합니다.

부모가 저장하면 자식 개체 목록이 자동으로 저장됩니다.

부모 측 :

    @Entity
    @Table(name="clients")
    public class Clients implements Serializable  {

         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)     
         @OneToMany(mappedBy="clients", cascade=CascadeType.ALL)
          List<SmsNumbers> smsNumbers;
    }

그리고 자식쪽에 다음 주석을 추가합니다.

  @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();    

 }

2
질문자가 귀하의 답변이 작동하는 방법과 이유를 알 수 있도록 답변을 자세히 설명하십시오.
UmarZaii

1 개의 클라이언트와 3 개의 SMS 번호를 삽입하고 그 후에 3 개의 SMS 번호의 fk를 업데이트합니다. 이것은 공연에 좋지 않습니다. Bidrectional에서 LinkManagement 사용
Tarunjeet Singh Salh 2010 년

1

setChilds에서 목록을 반복하고 다음과 같은 작업을 시도 할 수 있습니다.

child.parent = this;

또한 부모에 대한 캐스케이드를 적절한 값으로 설정해야합니다.


1

양방향 관계의 자식 개체에 부모 개체를 할당하는 방법은 다음과 같습니다.

일대 다라는 관계가 있다고 가정하면 각 상위 개체에 대해 하위 개체 집합이 존재합니다. 양방향 관계에서 각 자식 개체는 부모에 대한 참조를 갖습니다.

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 인터셉터는 DB 작업을 수행하기 전에 자신의 작업을 수행 할 수있는 API를 제공합니다. 객체의 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() );

유 구성을 저장하는 코드를 공유하시기 바랍니다 수
유 서프

0

org.hibernate.annotations일을 위해 사용 Cascade하고 hibernateJPA함께 사용하면 어떻게 든 자식 객체를 저장하는 것에 대해 불평합니다.


0

짧은 캐스케이드 유형을 all로 설정하면 작업을 수행합니다. 모델의 예입니다. 이와 같은 코드를 추가하십시오. @OneToMany (mappedBy = "영수증", cascade = CascadeType.ALL) 비공개 목록 saleSet;

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.