我很难创建关系的最佳JPA表示。似乎有很多方法可以做到这一点,我不确定哪个是最优的。没有JPA,我可能将结构表示为PERSON表和FRIEND表。假设PERSON表只有一个ID和NAME。 FRIEND表有一个OWNER_ID,一个PERSON_ID和一个可设置的布尔IS_ACTIVE字段。 OWNER_ID和PERSON_ID都引用PERSON表。一个人可以有很多朋友,所以唯一的主键是OWNER_ID和PERSON_ID。在我的代码中,我希望PersonEntity控制关系。 Java中的操作可能如下所示:
person1.getFriends().add( new Friend( person2, isActive ) );
Friend friend = person1.findFriend( person2ID );
这样的事情。请注意,我会喜欢 JPA来隐式分配朋友关系的OWNER。在上面的代码中,我没有在Friend构造函数中传递它,我只传递复合键的PERSON部分。
这是我对此的第一次猜测:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany( mappedBy="key.owner", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Friend> friends = new HashSet<>();
// getter and setter for Friends
...
}
@Entity
public class Friend {
@EmbeddedId
private Key key = new Key();
private boolean isActive;
...
@Embeddable
public static class Key implements Serializable {
@ManyToOne
private Person owner;
@OneToOne
private Person person;
...
}
}
但是当我使用这段代码时,我得到了堆栈溢出错误。我假设它被OneToOne关系搞糊涂了。
在JPA 2.0中建模这种关系的最佳方法是什么?我想最简单的方法是在Friend中引入生成的长键。但是,这似乎会使查询更加复杂,因为我将引入第三个映射表,基本上在SQL中映射关系两次。另一种方法是使其成为单向OneToMany关系,甚至不在Friend中明确指定OWNER。这将为映射引入另一个表,并使查询有点复杂。使用ElementCollection而不是OneToMany似乎是直截了当的,但后来我的理解是我无法将Friend作为一个实体来管理?
如果您需要更多细节或要求,请告诉我。顺便说一句,我尝试将一个MapsId放在朋友中为PKEY的所有者部分,但这给了我运行时错误。
感谢任何帮助。
注意:即使我向Friend添加了生成的密钥,我也希望在Friend实体中强制{owner,person}组合的唯一性。
答案 0 :(得分:3)
我确实会在Friend实体中添加一个自动生成的ID。它会使事情变得更简单(没有包含关联的复合键)。我不知道如何添加这样的ID(以及因此在Friend表中添加这样的附加列)会使任何查询更复杂,或者引入新表。它没有赢。您只需要一个额外的列,作为好友表的主键。
完成后,您需要修复映射:
mappedBy
应该在另一个实体中保存该字段的名称,该实体代表该关联的另一侧。在你的情况下,它就是&#34; key.owner&#34;。如果您停止使用复合键,它将只是&#34;所有者&#34;。最后,在双向OneToMany协会中,所有者总是多方(即朋友,在这种情况下)。因此,您必须初始化friend.owner字段才能保持关联。但是如果你封装了朋友列表而不是让它可以从oustide访问,那就很简单了。而不是做
person1.getFriends().add( new Friend( person2, isActive ) );
简单地做
person1.addFriend( new Friend( person2, isActive ) );
并确保addFriend()方法包含以下指令:
friendToAdd.setOwner(this);
答案 1 :(得分:1)
好吧,如果你想要一个解决这个问题的解决方案来创建一个人们希望在纯SQL中使用的表 - 这不是人工代理键和应用程序数据作为复合键 - 你绝对可以做到。但是,使用JPA会使它比您想要的更加混乱。
感谢@JB Niznet的出色表现,你也可以通过引入一个替代密钥来做到这一点。下面的解决方案只是消除了这种需求,据我所知,没有任何缺点。
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany( mappedBy="owner", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Friend> friends = new HashSet<>();
// getter and setter for Friends
...
}
@Entity
public class Friend {
@EmbeddedId
private Key key = new Key();
@ManyToOne
@Maps("ownerId")
private Person owner;
@ManyToOne
@MapsId("personId")
private Person person;
private boolean isActive;
...
@Embeddable
public static class Key implements Serializable {
private Long ownerId;
private Long personId;
...
}
}
答案 2 :(得分:0)
您必须考虑设计:实体将是类并具有表。关系只会有表格,因为关系只是连接2个实体! (除了关系具有属性,例如在这种情况下的“朋友评级”等。)
虽然Person
显然是实体,但friend
是一种关系。更重要的是,friend
是一种双向关系。 (A是B => B的朋友是A的朋友)
因此,您只需要通过人员列表扩展您的Person对象 - 称为朋友:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany
@JoinTable(name="friends")
@JoinColumn(name="person_A_id", referencedColumnName="id"), @JoinColumn(name="person_B_id", referencedColumnName="id"))
private Set<Person> friends = new HashSet<>();
}
这是不可测试的,但是应该给你一个这样的表:
person_a_id | person_b_id
1 2
1 6
2 7
然后你可以简单地使用你的实体,比如
Person A = new Person();
Person B = new Person();
A.friends.add(B);
然而,请记住,JPA将无法将此双向线程化:因此,如果您将B添加到A'S的朋友,您将不会在B的朋友列表中找到A.您必须注意这一点!