복붙노트

[SPRING] JPA @OneToMany -> 부모 - 하위 참조 (외래 키)

SPRING

JPA @OneToMany -> 부모 - 하위 참조 (외래 키)

나는 하위 엔티티를 참조하는 것에 관한 질문이 있거나 만약 내가 이런 식으로 뭔가 :

Parent.java:

@Entity(name ="Parent")
public class Parent {
    @Id
    @Generate.....
    @Column
    private int id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
    private Set<Child> children;

    simple ... getter and setter ...
}

그리고 Child.java :

@Entity(name ="Child")
public class Child{
    @Id
    @Generate....
    @Column
    private int id;

    @ManyToOne
    private Parent parent;

    ... simple getter an setter
}

다음 테이블이 생성됩니다.

Parent:
     int id

Child:
     int id
     int parent_id (foreign key: parent.id)

좋아, 지금까지 모든게 괜찮아. 그러나 Java에서이 Reference를 사용할 때, 이렇게 생각할 수 있습니다.

 @Transactional
 public void test() {
    Parent parent = new Parent();

    Child child = new Child();
    Set<Child> children = new HashSet<Child>();
    children.add(child);

    parent.setChildren(children);
    entityManager.persist(parent);
  }

이것은 데이터베이스에서 이것을 유도합니다 :

Parent:
     id
     100

Child
     id     paren_id
     101    100

그렇다고해서 부모님을 자녀에게 명시 적으로 설정해야합니다 (프레임 워크가 아마도 스스로 할 수 있다고 생각합니다).

그래서 데이터베이스에 실제로 무엇이 있습니다 :

Parent:
     id
     100

Child
     id     paren_id
     101    (null)

왜냐하면 나는 부모를 자녀에게 맡기지 않았기 때문이다. 그래서 내 질문 :

나는 정말로해야한다. 이거?

Parent.java:

...
setChildren(Set<Child> children) {
   for (Child child : children) {
     child.setParent.(this);
   }

   this.children = children;
}
...

편집하다:

빠른 답장에 따르면이 문제를 해결하기 위해 Reference-owing Entity에서 @JoinColumn을 사용하여 해결할 수있었습니다. 위에서 예제를 취하면 sth을 수행했습니다. 이렇게 :

Parent.java:

  @Entity(name ="Parent")
    public class Parent {
        @Id
        @Generate.....
        @Column
        private int id;

        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        @JoinColumn(name= "paren_id")
        private Set<Child> children;

        simple ... getter and setter ...
    }

그리고 Child.java :

@Entity(name ="Child")
public class Child{
    @Id
    @Generate....
    @Column
    private int id;

    ... simple getter an setter
}

이제 우리가 이렇게하면 :

 @Transactional
 public void test() {
    Parent parent = new Parent();

    Child child = new Child();
    Set<Child> children = new HashSet<Child>();
    children.add(child);

    parent.setChildren(children);
    entityManager.persist(parent);
  }

참조가 부모에 의해 올바르게 설정되었습니다.

Parent:
     id
     100

Child
     id     paren_id
     101    100

Answers에 감사드립니다.

해결법

  1. ==============================

    1.그건 하나의 전략입니다.

    그건 하나의 전략입니다.

    양방향 관계에는 관계의 "소유"및 "소유하지 않음"측면이 있습니다. 귀하의 케이스에 소유하고있는 측면이 자녀에 있기 때문에, 당신은 그것이 지속될 수 있도록 자녀와의 관계를 설정해야합니다. 소유 측은 일반적으로 @JoinColumn을 지정하는 위치에 따라 결정되지만, 해당 주석을 사용하는 것처럼 보이지 않으므로 부모 주석에서 mappedBy를 사용했기 때문에 유추 된 것일 수 있습니다.

    여기에 대한 자세한 내용은 여기를 참조하십시오.

  2. ==============================

    2.네, 그렇습니다. JPA는 엔티티 그래프의 일관성을 유지하지 않습니다. 특히 당신은 쌍방향 관계의 소유자 측 (당신의 경우에는 Child의 부모 속성에)을 설정해야합니다.

    네, 그렇습니다. JPA는 엔티티 그래프의 일관성을 유지하지 않습니다. 특히 당신은 쌍방향 관계의 소유자 측 (당신의 경우에는 Child의 부모 속성에)을 설정해야합니다.

    JPA 2.0 사양에서는 다음과 같은 단어가 있습니다.

  3. ==============================

    3.여전히 그렇다. 부모 엔티티에서 다음과 같은 것을 가질 수 있습니다.

    여전히 그렇다. 부모 엔티티에서 다음과 같은 것을 가질 수 있습니다.

    @PrePersist
    private void prePersist() {
       children.forEach( c -> c.setParent(this));
    }
    

    코드의 다른 위치에서 자식 / 부모 관계를 설정하기위한 코드를 반복하지 않도록합니다.

  4. ==============================

    4.위에 표시된 것과 같은 간단한 개체 그래프를 유지하면서 문제가 발생했습니다. H2에서 실행되면 모든 것이 작동하지만, MySQL에 대해 실행할 때 자식 테이블의 "paren_id"(@JoinColumn 주석에 정의 됨)는 생성 된 부모의 ID로 채워지지 않습니다. DB에 외래 키 제약 조건이있는 -null 열입니다.

    위에 표시된 것과 같은 간단한 개체 그래프를 유지하면서 문제가 발생했습니다. H2에서 실행되면 모든 것이 작동하지만, MySQL에 대해 실행할 때 자식 테이블의 "paren_id"(@JoinColumn 주석에 정의 됨)는 생성 된 부모의 ID로 채워지지 않습니다. DB에 외래 키 제약 조건이있는 -null 열입니다.

    다음과 같은 예외가 발생합니다.

    org.hibernate.exception.GenericJDBCException: Field 'paren_id' doesn't have a default value
    

    이 문제를 겪을만한 다른 사람들을 위해 우리가 발견 한 것은 @JoinColumn을 작동 시키려면 다른 속성을 사용해야한다는 것입니다.

    @JoinColumn(name="paren_id", nullable=false)
    
  5. ==============================

    5.EntityManager에 따르면, 트랜잭션의 삽입 순서를 관리하기를 원한다면 "tell him"해야한다. 그리고 당신은 그 일을하지 않으므로 "그"는 무엇을 유지해야할지 모르지만 부모님의 자녀 목록은 비어 있지 않으므로 "그 사람"은 올바른 것이지만 저장된 값은 null입니다.

    EntityManager에 따르면, 트랜잭션의 삽입 순서를 관리하기를 원한다면 "tell him"해야한다. 그리고 당신은 그 일을하지 않으므로 "그"는 무엇을 유지해야할지 모르지만 부모님의 자녀 목록은 비어 있지 않으므로 "그 사람"은 올바른 것이지만 저장된 값은 null입니다.

    따라서 다음과 같은 것을 고려해야합니다.

    ... begin, etc
    em.persist(child)
    em.persist(parent)
    

    여기에 부모 객체로 원하는 것을 수행하고 커밋하면 비슷한 경우에도 작동합니다.

  6. from https://stackoverflow.com/questions/9533676/jpa-onetomany-parent-child-reference-foreign-key by cc-by-sa and MIT license