web-dev-qa-db-ja.com

JPA @OneToMany->親-子参照(外部キー)

子エンティティからParentEntitiesを参照することについて質問があります。

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をChildに設定する必要があります(フレームワークはおそらくそれ自体でできると思います)。

データベースに実際にあるのはこれです:

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;
}
...

編集:

高速返信によると、参照所有エンティティの@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

回答ありがとうございます。

42
Sim0rn

本当にやらなきゃいけないの?このような?

それは一つの戦略です、はい。

双方向の関係には、関係の「所有」側と「非所有」側があります。あなたのケースの所有側はChildにあるため、永続化するためにそこに関係を設定する必要があります。所有側は通常、@JoinColumnを指定する場所によって決まりますが、その注釈を使用しているようには見えないため、mappedBy注釈でParentを使用したという事実から推測される可能性があります。

これについてもっと詳しく読む

14
beerbajay

まだそうであるようです。親Entityには次のようなものがあります

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

コードの他の場所で子/親の関係を設定するためのコードの繰り返しを避けるため。

4
pirho

はい、そうです。 JPAは、エンティティグラフの一貫性を気にしません。特に、双方向関係の所有者側に設定する必要があります(この場合、Childの親属性に設定します)。

JPA 2.0仕様では、これは次の言葉で言われています。

実行時の関係の一貫性を維持する責任があるのはアプリケーションであることに注意してください。たとえば、アプリケーションが更新されたときに、双方向関係の「1」側と「多」側が互いに整合することを保証します。実行時の関係。

3
Mikko Maunu

上記のような単純なオブジェクトグラフを保持しているときに問題が発生しました。 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)
2
Phil Brock

EntityManagerによると、正しく取得できた場合、トランザクションの挿入順序を管理したい場合は、子も保持する必要があることを「彼に伝える」必要があります。そして、あなたはそれをしていないので、「彼」は何を持続すべきかを知りませんが、あなたの親の子リストは空ではないので、「彼」はそれを取りますが、保存された値はnullです。

したがって、次のようなことを検討する必要があります。

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

ここで親オブジェクトで必要なことを実行してからコミットすると、これは同様の場合にも機能するはずです。

0
João Rodrigues