没有辅助连接表的Jpa ManyToMany自我参考

时间:2017-09-27 09:24:53

标签: java spring hibernate jpa persistence

我想告诉实体自己加入一个它在两个方向上都不是唯一的字段,但看起来互联网上的所有示例都使用了连接表,或者两者都很旧。 / p>

非规范化表:

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;

我想知道是否有办法映射我的案例。我知道它不是最好的拱形或性能最好的,并且可以使用不同的方法,但是我只是想知道那个特定的解决方案。这是对最复杂案例的简化。

2 个答案:

答案 0 :(得分:1)

我认为在你的案例中避免连接表绝对是一个坏主意。您当前的解决方案是最好的。

我认为你需要这样的东西:

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;

}

答案 1 :(得分:1)

免责声明:这个答案不是确定的。我为可读性写了答案,并且要在OP的评论中进行改进。此外,代码未经过测试。

数据库设计

作为答案,我将避免连接表并假设该表的设计如下:

  • 有两个表:personrole,各主键列PersonIdRoleId
  • 人员表中的
  • RoleIdParentRoleId是引用相同role.RoleId
  • 的外键
  • role表中的其他列(例如角色之间的关系)与问题
  • 无关

JPA

实体

实体遵循表格结构。角色实体将是一个基本实体:

@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
}

绕过连接表的技巧是利用与瞬态属性的@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();
    }
}

它也应该有效但性能明智,它可能会产生额外的无关计算。

对于您的示例

为了看它是否应该起作用,让我们做一个像草稿一样的纸+笔:

人员表

Person | Role | ParentRoleId
------ | ---- | ------------
   1   |   1  |     null
   2   |   1  |     null
   3   |   2  |      1
   4   |   2  |      1

角色表

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]
  

/!\小心初始化的东西(懒惰初始化可能很棘手)/!\

希望这有帮助