子エンティティから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
回答ありがとうございます。
本当にやらなきゃいけないの?このような?
それは一つの戦略です、はい。
双方向の関係には、関係の「所有」側と「非所有」側があります。あなたのケースの所有側はChild
にあるため、永続化するためにそこに関係を設定する必要があります。所有側は通常、@JoinColumn
を指定する場所によって決まりますが、その注釈を使用しているようには見えないため、mappedBy
注釈でParent
を使用したという事実から推測される可能性があります。
まだそうであるようです。親Entity
には次のようなものがあります
@PrePersist
private void prePersist() {
children.forEach( c -> c.setParent(this));
}
コードの他の場所で子/親の関係を設定するためのコードの繰り返しを避けるため。
はい、そうです。 JPAは、エンティティグラフの一貫性を気にしません。特に、双方向関係の所有者側に設定する必要があります(この場合、Childの親属性に設定します)。
JPA 2.0仕様では、これは次の言葉で言われています。
実行時の関係の一貫性を維持する責任があるのはアプリケーションであることに注意してください。たとえば、アプリケーションが更新されたときに、双方向関係の「1」側と「多」側が互いに整合することを保証します。実行時の関係。
上記のような単純なオブジェクトグラフを保持しているときに問題が発生しました。 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)
EntityManager
によると、正しく取得できた場合、トランザクションの挿入順序を管理したい場合は、子も保持する必要があることを「彼に伝える」必要があります。そして、あなたはそれをしていないので、「彼」は何を持続すべきかを知りませんが、あなたの親の子リストは空ではないので、「彼」はそれを取りますが、保存された値はnullです。
したがって、次のようなことを検討する必要があります。
... begin, etc
em.persist(child)
em.persist(parent)
ここで親オブジェクトで必要なことを実行してからコミットすると、これは同様の場合にも機能するはずです。