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