2016-08-10 1 views
11

"FriendRequest"と "ListOfFriends"の機能が必要です。フェイスブックと同様に、受信したフレンドリクエスト数と承認済みのフレンド数を表示します。Hibernateを使って友情関係を定義する方法は?

"FriendRequest"とは、ユーザーが受け取ったフレンドリクエストのリストを意味します。

"ListOfFriends"とは、ユーザーの友人のリストを持つことを意味します。

これを達成するには、次のクラスを定義しましたが、ユーザー名を使用してユーザーを取得しようとすると、「Stackoverflow」例外メッセージがスローされます。それは不定期なループになるようです。

toStringメソッドから "FriendRequests"と "Friends"を削除すると、例外がスローされなくなります。

This questionは、私が達成しようとしているものに似ていますが、違いはです。私は別の資格クラスを持つべきではありません。

メンバー

@Entity 
public class Member implements Serializable { 
    @Id 
    @GeneratedValue 
    private long id; 
    @Column(name = "username", nullable = false, unique = true) 
    private String username; 
    @Column(nullable = false) 
    private String fname; 
    @Column(nullable = false) 
    private String lname; 
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "requester") 
    private Set<Friendship> friendRequests = new HashSet<Friendship>(); 
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "friend") 
    private Set<Friendship> friends = new HashSet<Friendship>(); 

    getters and setters 

    @Override 
    public String toString() { 
     return "Member [ 
       .....     
       , fname=" + fname + ", lname=" + lname 
       // + friendRequests.size() + ", friends=" + friends.size() + 
     "]"; 
    } 
} 

友情

@Entity 
public class Friendship implements Serializable { 
    @Id 
    @ManyToOne 
    @JoinColumn(referencedColumnName = "username") 
    Member requester; 
    @Id 
    @ManyToOne 
    @JoinColumn(referencedColumnName = "username") 
    Member friend; 
    @Temporal(javax.persistence.TemporalType.DATE) 
    Date date; 
    @Column(nullable = false) 
    boolean active; 
+0

あなたが友人の要求のために別のテーブルを維持する必要があり、友情のためには、senderIdとreceiverIdだけのテーブルがあります。 –

+0

ここにテストケースコードを掲載できますか? –

+0

@NiravChhatrola私はまだテストケースを持っていません。 –

答えて

1

それがで仕事ができるように、私はあなたのコードに非常にいくつかの変更をしましたけれども、私は、あなたが持っている問題を得ることはありません私のセットアップ。問題のあるユースケースのためにいくつかのデータ、またはfindMembersを初期化するcreateFriendshipsのいずれかに注意を払う:

ここ

/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> 
    <persistence-unit name="hibernate-entitymanager-demo" transaction-type="RESOURCE_LOCAL"> 
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <properties> 
     <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/hibernate4?zeroDateTimeBehavior=convertToNull"/> 
     <property name="javax.persistence.jdbc.password" value="root"/> 
     <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> 
     <property name="javax.persistence.jdbc.user" value="root"/> 
     <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/> 
     <!-- property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/ --> 
    </properties> 
    </persistence-unit> 
</persistence> 

Main.java(2ユースケース。あなたがではなく、後者では)、最初にデータベースを作成したいと思うようにpersistence.xmlでjavax.persistence.schema-generation.database.actionプロパティは

public class Main { 

    public static void main(String[] args) { 
     EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate-entitymanager-demo"); 
     EntityManager em = emf.createEntityManager(); 

     EntityTransaction transaction = em.getTransaction(); 
     transaction.begin(); 

     // createFriendships(em); 

     findMembers(em); 

     transaction.commit(); 

     em.close(); 
     emf.close(); 
    } 

    private static void findMembers(EntityManager em) { 
     List<Member> list = em.createQuery("from Member").getResultList(); 
     for (Member m : list) { 
      System.out.println(m); 
     } 
    } 

    private static void createFriendships(EntityManager em) { 
     List<Member> members = createMembers(em); 

     for (int i = 0; i < members.size(); i++) { 
      for (int j = 0; j < members.size(); j++) { 
       if (i != j) { 
        createFriendship(em, members.get(i), members.get(j)); 
       } 
      } 
     } 
    } 

    private static List<Member> createMembers(EntityManager em) { 
     List<Member> members = new ArrayList<>(); 
     members.add(createMember(em, "Roberta", "Williams", "rwilliams")); 
     members.add(createMember(em, "Ken", "Williams", "kwilliams")); 
     members.add(createMember(em, "Dave", "Grossman", "dgrossman")); 
     members.add(createMember(em, "Tim", "Schafer", "tschafer")); 
     members.add(createMember(em, "Ron", "Gilbert", "rgilbert")); 
     return members; 
    } 

    private static Member createMember(EntityManager em, String fname, String lname, String username) { 
     Member m = new Member(); 
     m.setFname(fname); 
     m.setLname(lname); 
     m.setUsername(username); 
     em.persist(m); 
     return m; 
    } 

    private static void createFriendship(EntityManager em, Member requester, Member friend) { 
     Friendship f = new Friendship(); 
     f.setActive(true); 
     f.setDate(new Date()); 
     f.setRequester(requester); 
     f.setFriend(friend); 
     em.persist(f); 
    } 

} 

主な生産:

Member [fname = Roberta, lname = Williams, requests = 4, friends = 4] 
Member [fname = Ken, lname = Williams, requests = 4, friends = 4] 
Member [fname = Dave, lname = Grossman, requests = 4, friends = 4] 
Member [fname = Tim, lname = Schafer, requests = 4, friends = 4] 
Member [fname = Ron, lname = Gilbert, requests = 4, friends = 4] 

Friendship.java本当の実際の変更は、私は例外のcould not get a field value by reflection種類を得たように私はidusernameから変更され参照される列の名前でした。横に私はそれがデータベースの正規化の面でより良いことだと思う:

@Entity 
@Table(name = "FRIENDSHIPS") 
public class Friendship implements Serializable { 

    @Id 
    @ManyToOne 
    @JoinColumn(referencedColumnName = "id") 
    Member requester; 

    @Id 
    @ManyToOne 
    @JoinColumn(referencedColumnName = "id") 
    Member friend; 

    @Temporal(javax.persistence.TemporalType.DATE) 
    Date date; 

    @Column(nullable = false) 
    boolean active; 

    // getters & setters 

} 

Member.javatoString()を除いて、ここで変更なし、)

@Entity 
@Table(name = "MEMBERS") 
public class Member implements Serializable { 

    @Id 
    @GeneratedValue 
    private long id; 

    @Column(name = "username", nullable = false, unique = true) 
    private String username; 

    @Column(nullable = false) 
    private String fname; 

    @Column(nullable = false) 
    private String lname; 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "requester") 
    private Set<Friendship> friendRequests = new HashSet<>(); 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "friend") 
    private Set<Friendship> friends = new HashSet<>(); 

    // getters & setters 

    @Override 
    public String toString() { 
     return "Member [fname = " + fname + ", lname = " + lname 
       + ", requests = " + friendRequests.size() 
       + ", friends = " + friends.size() + "]"; 
    } 

} 
+0

ありがとう、私はそれを試みます。私は質問が不明確であるため、その言葉を改めただけです。 –

+0

あなたが言及した変更を適用しました。テーブルが空でも例外はスローされませんが、友人関係テーブルに新しい行を挿入すると、コードはStackoverflow例外をスローします。 –

1

私はHibernate 5.2.2.Finalの最新バージョンを使用して状況をエミュレート。私は同じ例外を得る。 EAGERLAZYに置き換えようとしたところ、うまく動作し始めました。 StackOverFlowErrorは、Hibernateが循環依存のオブジェクトを認識しなかったことを意味し、Stack Memoryがなくなるまで、参照された行を何度も何度も繰り返し取得して新しいオブジェクトと新しいオブジェクトを作成し始めます。

サネ状況:

Member[1] --ref-- Friendship[1] --ref-- Member[1] --ref-- ... 
(Here Hibernate recognizes cyclic dependency and creates only two objects) 

バグ状況(我々の場合):

Member[1] --ref-- Friendship[1] --ref-- Member[1] --ref-- ... 
(Here Hibernate doesn't recognize cyclic dependency and creates plenty of objects until Stack Memory licks, which cause StackOverFlowError) 
グーグル

asking私にこの答えた:すべてのJPAプロバイダは

をすべきですできる 環状の熱い負荷に対処する リレーション。問題がある場合は、JPA プロバイダに対してバグを報告する必要があります。

からNeil Stockton

まず解決策:

だから私は、あなたがLAZY初期化を使用することをお勧め。フェッチ操作はFETCH JOINで手動で処理します。これをどうぞご覧くださいanswer

第二の溶液:

循環参照を避けてください。あなたが最初に呼び出すプライマリオブジェクトがFriendshipである場合、FriendshipMemberをフェッチし、メンバはFriendshipについて何も知らないように、オブジェクトのFriendshipオブジェクトをコメントアウトします。それは私のために働く、私はテストしました。

残念ながら、あなたのアーキテクチャは、メンバーが主キーであるため、反対Friendship doesn't know about Memberを作ることはできません。私はあなたのアーキテクチャを変更することをお勧めします。


参考文献:

  • 私は相対questionを尋ねました。
  • LAZYフェッチを使用したサンプルエミュレーションのソースはhereです。
  • 報告bug against Hibernate
+0

この解決策は上記の問題のために働くでしょう。 –

3

私は、DBのデザインを提案したいと思います。

は今、あなたはPOJOクラスごとに友情テーブルや友人のテーブルを持っています。 その代わりに、もう1つの列のブール値isAccepted;(POJOクラスの変数)を持つフレンドシップテーブルのみを持つことができます。

ブール値がtrueの場合、メンバー(フレンド)は友人であることを意味します。 すべての友達を取得したいときは、isAcceptedをtrueに設定して友情の行を取得します。 friendrequests(または保留中のリクエストがApproved)のみを取得する場合は、isAcceptedをfalseに設定してすべての行を取得します。

現在のDBデザインまたはERモデルでは、あなたが使用しています。友人の要求を受け入れてどこかに保管すると、友人のテーブルからその行を削除する必要があります(それ以上の友人の要求ではありません)。友達はどこにいますか? あなたの現在のデザインによると、それは友人テーブルに格納されます。しかし、列が誰かの友人を示すという欄はありません。あるメンバーが他のメンバーのリクエストを受け入れて冗長な行を作成するたびに、友人のテーブルに行(友人)を追加するのと同じです。

0

実際、他の質問との違いは、資格証明オブジェクトを削除しただけではありません。メンバークラスはFriendshipと後方関係を持ち、循環依存関係を引き起こします。もう1つの例では、「ルート」オブジェクトは友人関係であり、メンバーとの結合を行います。メンバーを「ルート」にしたい場合は、別のモデルを設計する必要があります。

関連する問題