如何使用Hibernate定义友谊关系?

我需要有“FriendRequest”和“ListOfFriends”function。类似于facebook,它显示收到的朋友请求的数量和已批准的朋友的数量。

通过“FriendRequest”,我的意思是拥有用户收到的朋友请求列表。

通过“ListOfFriends”,我的意思是拥有用户的朋友列表。

为了实现这一点,我定义了以下类,但是当我尝试使用其用户名检索用户时,将抛出“Stackoverflow”exception消息。 它似乎进入了一个无限循环。

当我从我的toString方法中删除“FriendRequests”和“Friends”时,它会停止抛出exception。

这个问题类似于我要实现的目标,但不同之处在于我不具备单独的Credential类。

会员

 @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 friendRequests = new HashSet(); @OneToMany(fetch = FetchType.EAGER, mappedBy = "friend") private Set friends = new HashSet(); 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; 

我想建议DB Design。

现在,根据POJO课程,你有一个友谊表和朋友表。 而不是那样,你可以只拥有一个友谊表,其中一列是boolean isAccepted;(POJO类中的变量)。

如果布尔值为true,则表示该成员(朋友)是朋友。 当你想要所有的朋友时,检索isAccepted设置为true的友谊行; 如果您只想获取friendrequests(或待批准的待处理请求),请将isAccepted设置为false的所有行。

使用当前的DB设计或ER模型。 您需要从友谊表中删除该行(因为它不再是朋友请求),如果该人接受了该朋友请求并存储在某处。 你在哪里存放朋友。 根据您目前的设计,它存储在朋友表中。 但是没有专栏说该行表示某人的朋友。 这就像每次一个成员接受其他成员的请求并且只是创建冗余行时向friend表添加一行(朋友)。

我没有遇到你的问题,虽然我对你的代码做了很少的修改,所以它可以用我的设置。

/META-INF/persistence.xml

    org.hibernate.ejb.HibernatePersistence           

Main.java (这里有两个用例:要么初始化一些数据的createFriendships,要么是有问题的用例的findMembers。请注意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 list = em.createQuery("from Member").getResultList(); for (Member m : list) { System.out.println(m); } } private static void createFriendships(EntityManager em) { List 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 createMembers(EntityManager em) { List 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实际的实际更改是引用的列名,我从username更改为id因为我得到了一个could not get a field value by reflectionexceptioncould not get a field value by reflection 。 除了我认为在数据库规范化方面更好:

 @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.java (这里没有变化,除了toString()

 @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 friendRequests = new HashSet<>(); @OneToMany(fetch = FetchType.EAGER, mappedBy = "friend") private Set friends = new HashSet<>(); // getters & setters @Override public String toString() { return "Member [fname = " + fname + ", lname = " + lname + ", requests = " + friendRequests.size() + ", friends = " + friends.size() + "]"; } } 

我用最新版本的Hibernate 5.2.2.Final模拟了你的情况。 我得到同样的例外。 我试图用EAGER取代LAZY取代它开始工作正常。 StackOverFlowError意味着Hibernate不能识别循环依赖中的对象,并且开始一次又一次地获取引用的行,直到Stack Memory逃跑为止创建新的和新的对象。

Sane情况:

 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) 

谷歌搜索和询问给了我这个答案:

所有JPA提供者都应该能够应对循环关系的急切加载。 任何问题都应该意味着您提出了针对JPA提供程序的错误。

– 尼尔斯托克顿

第一解决方案

所以我建议你使用LAZY初始化。 并通过FETCH JOIN手动处理获取操作。 请看这个答案 。

二解决方案:

避免循环引用。 如果您首先调用的主要对象是Friendship则在对象中注释友谊对象,以便Friendship将获取Member而会员将对Friendship一无所知。 它适用于我,我已经测试过了。

不幸的是,你的架构不允许让对方的Friendship doesn't know about Member因为会员是主要的钥匙。 我建议你改变你的架构它是非常强大的耦合 – 编程的坏习惯。


参考文献:

  • 我问了相关问题 。
  • 使用LAZY获取的示例模拟的来源就在这里 。
  • 报告了针对Hibernate的bug 。

实际上,与其他问题的区别不仅仅是删除了Credentials对象。 您的Member类与Friendships有向后关系,这会导致循环依赖。 在另一个例子中,“根”对象是友谊,它与成员联系。 如果你想让一个成员成为“根”,你应该设计一个不同的模型。