[英]Jpa ManyToMany Self Reference without auxiliar Join Table
I'd like to tell entity to join with itself for a field that it's not unique in both directions, but it looks like all examples in internet use a Join table or either are pretty old. 我想告诉实体在两个方向上都不都是唯一的实体,但是看起来互联网上的所有示例都使用Join表,或者其中一个都已经很旧了。
Person Denormalized Table: 人非正规化表:
PersonId (Pk) | RoleId | ParentRoleId
1 1 NULL
2 1 NULL
3 2 1
4 2 1
Person Entity (With mappings that seem to load an empty list): 人员实体(带有似乎加载了空列表的映射):
@Column
private Long personId;
@Column
private Long roleId;
@Column
private Long parentRoleId;
@ManyToMany
@JoinColumn(name = "parentRoleId", referencedColumnName = "roleId", updatable = false, insertable = false)
private List<Person> personsWithParentRole;
@ManyToMany
@JoinColumn(name = "roleId", referencedColumnName = "parentRoleId", updatable = false, insertable = false)
private List<Person> personsWhoseRoleHasCurrentPersonRoleAsParent;
I would like to know if there's a way to map my case. 我想知道是否有一种方法可以说明我的情况。 I know it's not the best arch or the most performant, and that one can use a different approach, but please I am just wondering about that particular solution.
我知道这不是最好的架构或性能最高的,并且可以使用其他方法,但是请问我只是想知道这种特定的解决方案。 This is a simplification from a most complex case.
这是最复杂情况的简化。
I think is definitly a bad idea to avoid a join table in your case. 我认为绝对避免在您的情况下使用联接表是一个坏主意。 Your current solution is the best.
您当前的解决方案是最好的。
I think you need something like this: 我认为您需要这样的东西:
public class Person {
@Id
private long id;
@JoinTable(name = "person_links", joinColumns = {
@JoinColumn(name = "subordinate", referencedColumnName = "id", nullable = false)}, inverseJoinColumns = {
@JoinColumn(name = "manager", referencedColumnName = "id", nullable = false)})
@ManyToMany
private List<Person>subordinates;
@ManyToMany(mappedBy = "subordinates")
private List<Person> managers;
}
Disclaimer : this answer is not definitive. 免责声明 :此答案不是确定的。 I write the answer for readibility sake and it is meant to be improved along the comment with OP.
我出于可读性考虑而写答案,并且打算与OP一起进行注释。 Furthermore, code is not tested.
此外,未测试代码。
for the answer, I'll avoid the join table and assume that table is designed as followed: 对于答案,我将避免使用联接表,并假设该表的设计如下:
person
and role
with respective primary key columns PersonId
and RoleId
person
和role
以及各自的主键列PersonId
和RoleId
RoleId
and ParentRoleId
are foreign keys referring to the same role.RoleId
RoleId
和ParentRoleId
是引用相同role.RoleId
外键。 role
table (eg relationship between roles) are irrelevant for the question role
表中的其他列(例如角色之间的关系)与该问题无关 Entities follow table structure. 实体遵循表结构。 The Role entity would be a basic entity:
角色实体将是一个基本实体:
@Entity
public class Role{
// ---- JPA attributes
@Id
// ...
@Column(...)
private Long roleId;
@OneToMany(mappedBy = "role")
private List<Person> personsWithThisRoleAsPrimaryRole;
@OneToMany(mappedBy = "parentRole")
private List<Person> personsWithThisRoleAsParentRole;
// ---- Constructor
public Role(){
// your initialisation
// initialise list to avoid NullPointerException
this.personsWithThisRoleAsPrimaryRole = new ArrayList<>();
this.personsWithThisRoleAsParentRole = new ArrayList<>();
}
// getters & setters
}
The trick for bypassing the join table would be leveraging the @OneToMany
relationship with a transient attribute: 绕过
@OneToMany
表的技巧是利用@OneToMany
关系和一个过渡属性:
@Entity
public class Person{
// ---- JPA attributes
@Id
// ...
@Column(...)
private Long personId;
@ManyToOne
@JoinColumn(name = "RoleId")
private Role role;
@ManyToOne
@JoinColumn(name = "ParentRoleId")
private Role parentRole;
// ---- Transient attributes
@Transient
private List<Person> personsWithParentRole;
@Transient
private List<Person> personsWhoseRoleHasCurrentPersonRoleAsParent;
// ---- Constructor
public Person(){
// your initialisation
// initialise list to avoid NullPointerException
this.personsWithParentRole = new ArrayList<>();
this.personsWhoseRoleHasCurrentPersonRoleAsParent = new ArrayList<>();
}
@PostLoad
public void postLoad(){
// during JPA initialisation, role and parentRole have been defined
// if the value exist in the database. Consequently, we can fetch some
// interesting info:
if(role != null){
personsWithParentRole.addAll(role.getPersonsWithThisRoleAsParentRole());
}
if(parentRole != null){
personsWhoseRoleHasCurrentPersonRoleAsParent.addAll(parentRole.getPersonsWithThisRoleAsPrimaryRole());
}
}
// getters and setters for JPA attributes
// getters for transient attributes. It doesn't make sense to create the setters for the transient list here.
}
Transient attributes have to be used with care as I encountered many fancy problems. 当我遇到许多奇特的问题时,必须谨慎使用瞬态属性。 However, they are useful as you may fetch the persons list once.
但是,它们很有用,因为您可以一次获取人员列表。 If you had something like:
如果您有类似以下内容:
public List<Person> getPersonsWithParentRole{
if(role != null){
return role.getPersonsWithThisRoleAsParentRole();
}
}
public List<Person> getPersonsWithParentRole{
if(parentRole != null){
return parentRole.getPersonsWithThisRoleAsPrimaryRole();
}
}
It should also work but performance wise, it may engender additional irrelevant calculations. 它也应该起作用,但是从性能角度来看,可能会导致其他不相关的计算。
To see if it should work, let's do a paper+pen like draft: 要查看它是否应该工作,让我们像草稿一样做纸质笔:
Person table 人表
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
Role table 角色表
Role | Additional Columns
---- | ----------------
1 | ...
2 | ...
Entity-wise 实体明智
Person entity without considering the @PostLoad
and transient lists: 人员实体,不考虑
@PostLoad
和瞬态列表:
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
the role entities with the @OneToMany
relationships: 具有
@OneToMany
关系的角色实体:
Role | PersonsWithThisRoleAsPrimaryRole | PersonsWithThisRoleAsParentRole
---- | -------------------------------- | -------------------------------
1 | [1, 2] | [3, 4]
2 | [3, 4] | [empty]
Consequently, after @postLoad
, you'll have: 因此,在
@postLoad
之后,您将拥有:
Person | Role | ParentRoleId | PersonsWithParentRole | PersonsWhoseRoleHasCurrentPersonRoleAsParent
------ | ---- | ------------ | --------------------- | --------------------------------------------
1 | 1 | null | [3,4] | [empty]
2 | 1 | null | [3,4] | [empty]
3 | 2 | 1 | [empty] | [1, 2]
4 | 2 | 1 | [empty] | [1, 2]
/!\\ Be careful about initialisation stuff (Lazy initialisation can be tricky) /!\\
/!\\注意初始化的内容(延迟初始化可能很棘手)/!\\
Hope this helps 希望这可以帮助
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.