3

リレーションシップの最適な JPA 表現を作成するのに苦労しています。いろいろな方法があるようで、どれが最適なのかわかりません。JPA がなければ、構造を PERSON テーブルと FRIEND テーブルとして表すことができます。PERSON テーブルに ID と NAME だけがあるとします。FRIEND テーブルには、OWNER_ID、PERSON_ID、および設定可能なブール値の IS_ACTIVE フィールドがあります。OWNER_ID と PERSON_ID はどちらも PERSON テーブルを参照します。人は多くの友達を持つことができるので、一意の主キーは OWNER_ID と PERSON_ID になります。私のコードでは、PersonEntity で関係を制御したいと考えています。Java での操作は次のようになります。

person1.getFriends().add( new Friend( person2, isActive ) );
Friend friend = person1.findFriend( person2ID );

そんな感じ。JPA にフレンド関係の OWNER を暗黙的に割り当ててもらいたいことに注意してください上記のコードでは、Friend コンストラクターには渡さず、複合キーの PERSON 部分のみを渡します。

これが私の最初の推測でした:

@Entity
public class Person {
   @Id
   @GeneratedValue
   private Long id;

    @OneToMany( mappedBy="key.owner", cascade=CascadeType.ALL, orphanRemoval = true)
    private Set<Friend> friends = new HashSet<>();

    // getter and setter for Friends
    ...
}

@Entity
public class Friend {
   @EmbeddedId
   private Key key = new Key();

   private boolean isActive;

   ...

   @Embeddable
   public static class Key implements Serializable {
      @ManyToOne
      private Person owner;

      @OneToOne
      private Person person;

      ...
   }
}

しかし、このコードを使用すると、スタック オーバーフロー エラーが発生します。OneToOneの関係で混乱していると思います。

JPA 2.0でこの種の関係をモデル化する最良の方法は何ですか? 最も簡単なのは、生成された長いキーを Friend に導入することだと思います。しかし、「ジョンの友達を見つける」のクエリがより複雑になるようです。これは、基本的に SQL で関係を 2 回マッピングする 3 番目のマッピング テーブルを導入するためです。もう 1 つの方法は、一方向の OneToMany 関係にし、Friend で明示的に OWNER を指定することさえしないことです。これにより、マッピング用に別のテーブルが導入され、クエリが少し複雑になります。OneToMany の代わりに ElementCollection を使用するのは簡単に思えますが、私の理解では、Friend をエンティティとして管理できませんでした。

詳細や要件が必要な場合はお知らせください。ところで、PKEY の所有者部分の Friend に MapsId を入れようとしましたが、実行時エラーが発生しました。

どんな助けでも大歓迎です。

注: 生成されたキーを Friend に追加しても、Friend エンティティの {owner,person} の組み合わせに一意性を適用したいと考えています。

4

3 に答える 3

2

さて、純粋な SQL で作成したいようなテーブルを作成するこの問題の解決策が必要な場合は、人工的な代理キーではなく、アプリケーション データを複合キーとして作成します。間違いなく実行できます。ただし、JPA を使用すると、必要以上にややこしくなります。

@JB Niznet のすばらしい意見に感謝します。彼が提案するように、代理キーを導入することでこれを行うこともできます。以下の解決策はその必要性を取り除くだけであり、私が知る限り、欠点はありません.

@Entity
public class Person {
   @Id
   @GeneratedValue
   private Long id;

   @OneToMany( mappedBy="owner", cascade=CascadeType.ALL, orphanRemoval = true)
   private Set<Friend> friends = new HashSet<>();

   // getter and setter for Friends
   ...
}

@Entity
public class Friend {
   @EmbeddedId
   private Key key = new Key();

   @ManyToOne
   @Maps("ownerId")
   private Person owner;

   @ManyToOne
   @MapsId("personId")
   private Person person;

   private boolean isActive;

   ...

   @Embeddable
   public static class Key implements Serializable {
      private Long ownerId;
      private Long personId;

      ...
   }
} 
于 2013-02-01T16:55:24.127 に答える
1

設計について考える必要があります。エンティティはクラスであり、テーブルがあります。リレーションは 2 つのエンティティを接続しているだけなので、リレーションにはテーブルしかありません! (ただし、関係には、この場合の「友人の評価」などの属性があります。)

は明らかにPersonエンティティですfriendが、関係です。それ以上friendは、双方向の関係です。(A は B の友達 => B は A の友達)

そのため、友達と呼ばれる人物のリストによって、人物オブジェクトを拡張する必要があります。

@Entity
public class Person {
   @Id
   @GeneratedValue
  private Long id;

  @OneToMany
  @JoinTable(name="friends")
  @JoinColumn(name="person_A_id", referencedColumnName="id"), @JoinColumn(name="person_B_id", referencedColumnName="id")) 
  private Set<Person> friends = new HashSet<>();

}

これは未テストですが、次のような表が表示されるはずです。

person_a_id | person_b_id
1             2
1             6
2             7

次に、次のようにエンティティを簡単に操作できます

Person A = new Person();
Person B = new Person();
A.friends.add(B);

ただし、JPA はこの双方向スレッドを実行できないことに注意してください。したがって、A の友人に B を追加すると、B の友人のリストに A が見つかりません。注意が必要です。

于 2013-01-31T20:32:12.420 に答える