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

Java 如何使用Hibernate定义友谊关系?,java,hibernate,orm,hibernate-mapping,Java,Hibernate,Orm,Hibernate Mapping,我需要有“FriendRequest”和“ListOfFriends”功能。类似于facebook,它显示收到的好友请求数和批准的好友数 所谓“FriendRequest”,我的意思是有一个用户收到的朋友请求列表 通过“ListOfFriends”,我的意思是有一个用户的朋友列表 为了实现这一点,我定义了以下类,但当我尝试使用用户名检索用户时,将抛出一条“Stackoverflow”异常消息。它似乎进入了一个不确定的循环 当我从toString方法中删除“FriendRequests”和“Fri

我需要有“FriendRequest”和“ListOfFriends”功能。类似于facebook,它显示收到的好友请求数和批准的好友数

所谓“FriendRequest”,我的意思是有一个用户收到的朋友请求列表

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

为了实现这一点,我定义了以下类,但当我尝试使用用户名检索用户时,将抛出一条“Stackoverflow”异常消息。它似乎进入了一个不确定的循环

当我从
toString
方法中删除“FriendRequests”和“Friends”时,它停止抛出异常

与我将要实现的目标类似,但区别在于我不知道有什么单独的凭证类。

成员

@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() + 
       "]";
    }
}

我不明白你有什么问题,尽管我对你的代码做了很少的修改,所以它可以与我的设置一起工作

/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>
主要产品:

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]
Friendly.java真正的实际更改是引用的列名,我将其从
username
更改为
id
,因为我得到了一个
无法通过反射获得字段值的异常。此外,我认为在数据库规范化方面更好:

@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()
,此处没有任何更改)

@实体
@表(name=“成员”)
公共类成员实现可序列化{
@身份证
@生成值
私人长id;
@列(name=“username”,nullable=false,unique=true)
私有字符串用户名;
@列(nullable=false)
私有字符串fname;
@列(nullable=false)
私有字符串名称;
@OneToMany(fetch=FetchType.EAGER,mappedBy=“请求者”)
private Set friendRequests=new HashSet();
@OneToMany(fetch=FetchType.EAGER,mappedBy=“friend”)
private Set friends=new HashSet();
//接球手和接球手
@凌驾
公共字符串toString(){
return“Member[fname=“+fname+”,lname=“+lname
+“,requests=“+friendRequests.size()
+,friends=“+friends.size()+”];
}
}

我用最新版本的
Hibernate 5.2.2.Final
模拟了您的情况。我也有同样的例外。我试着用
eanger
取回
LAZY
来代替
eanger
,它开始正常工作
StackOverflowerError
意味着Hibernate无法识别循环依赖中的对象,并开始一次又一次地获取引用行,创建新的对象,直到
堆栈内存
耗尽

理智的情况:

错误情况(我们的案例):

谷歌搜索并给了我这个答案:

所有JPA提供商都应该能够处理循环负载 关系任何问题都应该意味着你对JPA提出了一个bug 提供者

第一个解决方案:

因此,我建议您使用
LAZY
初始化。并通过手动处理获取操作。请看这个

第二种解决方案:

避免循环引用。若您将首先调用的主要对象是
友谊
,则注释掉对象中的友谊对象,这样
友谊
将获取
成员
,而成员将对
友谊
一无所知。它对我有效,我已经测试过了

不幸的是,您的体系结构不允许做相反的事情,因为成员是主键,所以友谊不了解成员。我建议你改变你的架构,这是一个非常强的耦合——编程的糟糕做法


参考文献:

  • 我问亲戚
  • 使用
    LAZY
    抓取的示例仿真源代码为
  • 报道

    • 我想建议DB设计

      现在,根据POJO类,您有一个友谊表和朋友表。 取而代之的是,您可以只接受一个以上列布尔值的友谊表;(POJO类中的变量)

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

      对于当前的DB设计或ER模型,您正在使用。如果此人接受好友请求并将其存储在某处,则需要从友谊表中删除该行(因为它不再是好友请求)。你把朋友存放在哪里。
      根据您目前的设计,它存储在friends表中。但没有列表明该行表示某人的朋友。这就像每次一个成员接受另一个成员的请求时,在friends表中添加一行(friend)一样,只是做了多余的行。

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

      最好的方法是你需要为朋友请求和友谊维护另一个表。有一个表只有senderId和receiverId。你能在这里发布你的测试用例代码吗?@NiravChhatrola我还没有任何测试用例。@ParthSolanki你能进一步说明吗?检查FetchType lazy。谢谢,我试试看。我只是重新表述了这个问题,因为它不清楚。我应用了您提到的更改,当表为空时,不会引发异常,但当我向Friendly表插入新行时,代码会引发Stackoverflow异常。
      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]
      
      @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
      
      }
      
      @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() + "]";
          }
      
      }
      
      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)