我需要“FriendRequest”和“ListOfFriends”功能。类似于facebook,它显示收到的朋友请求数和已批准的朋友数。
通过“FriendRequest”,我的意思是拥有用户收到的好友请求列表。
通过“ListOfFriends”,我的意思是拥有用户的朋友列表。
为了实现这一点,我定义了以下类,但是当我尝试使用其用户名检索用户时,将抛出“Stackoverflow”异常消息。它似乎进入了一个无限循环。
当我从toString
方法中删除“FriendRequests”和“Friends”时,它会停止抛出异常。
This question与我要实现的相似,但区别在于我没有单独的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<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 :(得分:3)
我想建议DB Design。
现在,根据POJO课程,你有一个友谊表和朋友表。 而不是那样,你可以只拥有一个友谊表,其中包含另一列boolean isAccepted;(POJO类中的变量)。
如果布尔值为true,则表示该成员(朋友)是朋友。 当你想要所有的朋友时,检索isAccepted设置为true的友谊行; 如果您只想获取friendrequests(或待批准的待处理请求),请将isAccepted设置为false的所有行。
使用当前的DB设计或ER模型。您需要从友谊表中删除该行(因为它不再是朋友请求),如果该人接受该朋友请求并存储在某处。你在哪里存放朋友。 根据您目前的设计,它存储在朋友表中。但是没有专栏说该行表示某人的朋友。这就像每次一个成员接受其他成员的请求并且只是创建冗余行时向friend表添加一行(朋友)。
答案 1 :(得分:1)
我没有遇到您遇到的问题,但我对您的代码进行了很少的修改,因此可以使用我的设置。
<强> /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 (这里有两个用例:createFriendships用于初始化一些数据,或者findMembers用于有问题的用例。请注意persistence.xml中的javax.persistence.schema-generation.database.action
属性。 #39;我想在第一个数据库中创建数据库,而不是在后者中创建数据库
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 实际的实际更改是我引用的列名称,我从username
更改为id
,因为我得到了could 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<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() + "]";
}
}
答案 2 :(得分:1)
我用最新版本的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)
谷歌搜索和asking给了我这个答案:
所有JPA提供商都应该能够应对急切的循环加载 关系。任何问题都应该意味着你提出了一个针对JPA的错误 提供商。
第一个解决方案:
所以我建议您使用LAZY
初始化。并由FETCH JOIN手动处理提取操作。请参阅此answer。
第二个解决方案:
避免循环引用。如果您首先调用的主要对象是Friendship
,则在对象中注释掉友谊对象,以便Friendship
获取Member
,而会员将对Friendship
一无所知。它适用于我,我已经测试过了。
不幸的是,由于Member是主键,因此架构不允许使用相反的Friendship doesn't know about Member
。我建议你改变你的架构它是非常强大的耦合 - 编程的坏习惯。
<强>参考文献:强>
LAZY
抓取的示例模拟来源为here。答案 3 :(得分:0)
实际上,与其他问题的区别不仅在于删除了凭据对象。您的Member类与Friendships有向后关系,这会导致循环依赖。在另一个例子中,&#34; root&#34;对象是一种友谊,它与成员联系。如果你想让一个会员成为&#34; root&#34;,你应该设计一个不同的模型。