没有辅助连接的 Jpa ManyToMany 自引用 Table
Jpa ManyToMany Self Reference without auxiliar Join Table
我想告诉实体为一个字段与自身连接,它在两个方向上都不是唯一的,但看起来互联网上的所有示例都使用 Join table 或其中任何一个都很旧。
人物非规范化Table:
PersonId (Pk) | RoleId | ParentRoleId
1 1 NULL
2 1 NULL
3 2 1
4 2 1
人 实体(使用似乎加载空列表的映射):
@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;
我想知道是否有办法映射我的案例。我知道它不是最好的拱门或最高性能的,并且可以使用不同的方法,但我只是想知道那个特定的解决方案。这是对最复杂案例的简化。
我认为在您的情况下避免加入 table 绝对不是一个好主意。您当前的解决方案是最好的。
我想你需要这样的东西:
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;
}
免责声明:这个答案不是确定的。为了便于阅读,我写下了答案,并且打算随着 OP 的评论进行改进。此外,代码未经过测试。
数据库设计
对于答案,我将避免 join table 并假设 table 设计如下:
- 有两个 table:
person
和 role
分别具有主键列 PersonId
和 RoleId
- 在人 table 中,
RoleId
和 ParentRoleId
是指向相同 role.RoleId
的外键
role
table 中的其他列(例如角色之间的关系)与问题无关
JPA
实体
实体遵循 table 结构。角色实体将是一个基本实体:
@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
}
绕过连接 table 的技巧是利用具有瞬态属性的 @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.
}
瞬态属性
临时属性必须小心使用,因为我遇到了很多奇特的问题。但是,它们很有用,因为您可以一次获取人员列表。如果你有类似的东西:
public List<Person> getPersonsWithParentRole{
if(role != null){
return role.getPersonsWithThisRoleAsParentRole();
}
}
public List<Person> getPersonsWithParentRole{
if(parentRole != null){
return parentRole.getPersonsWithThisRoleAsPrimaryRole();
}
}
它应该也能工作,但在性能方面,它可能会产生额外的无关计算。
以你为例
为了看看它是否可行,让我们做一个纸+笔的草稿:
个人table
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
角色table
Role | Additional Columns
---- | ----------------
1 | ...
2 | ...
实体方面
不考虑 @PostLoad
和临时列表的个人实体:
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
具有@OneToMany
关系的角色实体:
Role | PersonsWithThisRoleAsPrimaryRole | PersonsWithThisRoleAsParentRole
---- | -------------------------------- | -------------------------------
1 | [1, 2] | [3, 4]
2 | [3, 4] | [empty]
因此,在 @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) /!\
希望这对您有所帮助
我想告诉实体为一个字段与自身连接,它在两个方向上都不是唯一的,但看起来互联网上的所有示例都使用 Join table 或其中任何一个都很旧。
人物非规范化Table:
PersonId (Pk) | RoleId | ParentRoleId
1 1 NULL
2 1 NULL
3 2 1
4 2 1
人 实体(使用似乎加载空列表的映射):
@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;
我想知道是否有办法映射我的案例。我知道它不是最好的拱门或最高性能的,并且可以使用不同的方法,但我只是想知道那个特定的解决方案。这是对最复杂案例的简化。
我认为在您的情况下避免加入 table 绝对不是一个好主意。您当前的解决方案是最好的。
我想你需要这样的东西:
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;
}
免责声明:这个答案不是确定的。为了便于阅读,我写下了答案,并且打算随着 OP 的评论进行改进。此外,代码未经过测试。
数据库设计
对于答案,我将避免 join table 并假设 table 设计如下:
- 有两个 table:
person
和role
分别具有主键列PersonId
和RoleId
- 在人 table 中,
RoleId
和ParentRoleId
是指向相同role.RoleId
的外键
role
table 中的其他列(例如角色之间的关系)与问题无关
JPA
实体
实体遵循 table 结构。角色实体将是一个基本实体:
@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
}
绕过连接 table 的技巧是利用具有瞬态属性的 @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.
}
瞬态属性
临时属性必须小心使用,因为我遇到了很多奇特的问题。但是,它们很有用,因为您可以一次获取人员列表。如果你有类似的东西:
public List<Person> getPersonsWithParentRole{
if(role != null){
return role.getPersonsWithThisRoleAsParentRole();
}
}
public List<Person> getPersonsWithParentRole{
if(parentRole != null){
return parentRole.getPersonsWithThisRoleAsPrimaryRole();
}
}
它应该也能工作,但在性能方面,它可能会产生额外的无关计算。
以你为例
为了看看它是否可行,让我们做一个纸+笔的草稿:
个人table
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
角色table
Role | Additional Columns
---- | ----------------
1 | ...
2 | ...
实体方面
不考虑 @PostLoad
和临时列表的个人实体:
Person | Role | ParentRoleId
------ | ---- | ------------
1 | 1 | null
2 | 1 | null
3 | 2 | 1
4 | 2 | 1
具有@OneToMany
关系的角色实体:
Role | PersonsWithThisRoleAsPrimaryRole | PersonsWithThisRoleAsParentRole
---- | -------------------------------- | -------------------------------
1 | [1, 2] | [3, 4]
2 | [3, 4] | [empty]
因此,在 @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) /!\
希望这对您有所帮助