【问题标题】:Jpa ManyToMany Self Reference without auxiliar Join Table没有辅助连接表的 Jpa ManyToMany 自引用
【发布时间】:2017-09-27 09:24:52
【问题描述】:

我想告诉实体加入一个字段,它在两个方向上都不是唯一的,但看起来互联网上的所有示例都使用 Join 表,或者两者都相当旧。

Person 非规范化表:

PersonId (Pk) | RoleId | ParentRoleId
  1               1         NULL
  2               1         NULL
  3               2          1
  4               2          1

Person 实体(带有似乎加载空列表的映射):

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

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

【问题讨论】:

  • 如果我理解属性,这意味着对于人员 1 和 2,personsWithParentRole 是空的,但 personWhoseRoleHasCurrentPersonRoleAsParent 包含人员 3 和 4。反之亦然,对于人员 3 和 4,personWhoseRoleHasCurrentPersonRoleAsParent 是为空,但 personsWithParentRole 包含人 1 和 2?
  • 没错,感谢您的努力!
  • 因为发表我的想法作为评论会很糟糕,所以我发布了一个答案。请让我知道您对此的看法。我想提醒一下,我不习惯这种做法,但我喜欢你提出的挑战。
  • 如果你碰巧使用 PostgreSQL,也许你可以have a look toward views 但我从来没有设法找到工作情况

标签: java spring hibernate jpa persistence


【解决方案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;

}

【讨论】:

  • 对不起,我忘了提到我上面写的代码不起作用,即使我检查并重新检查了数据,它也会加载空列表并且应该可以工作
  • 你能发布一份你的需求吗?
  • 我更新了字段的名称以更准确。我不明白为什么我不能使用整个 Person 实体而不是 Long,在 OneToMany 或 OneToOne 关系中,您可以指示长连接列,它知道您想要整个实体
【解决方案2】:

免责声明:这个答案不是确定的。为了便于阅读,我写了答案,它的目的是随着 OP 的评论进行改进。此外,代码没有经过测试。


数据库设计

对于答案,我将避免 join table 并假设该表的设计如下:

  • 有两个表:personrole,主键列分别为 PersonIdRoleId
  • 在person表中,RoleIdParentRoleId是指同一个role.RoleId的外键
  • role 表中的其他列(例如角色之间的关系)与问题无关

JPA

实体

实体遵循表结构。 Role 实体将是一个基本实体:

@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 实体:

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]

/!\ 注意初始化的东西(延迟初始化可能很棘手)/!\

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多