简体   繁体   English

JPA /休眠:ManyToMany删除联接表上的关系

[英]JPA/Hibernate: ManyToMany delete relation on Join Table

Disclaimer: This is my first Java project; 免责声明:这是我的第一个Java项目。 learning as I go. 边走边学。

Background: I've inherited a legacy database on which to build a new RESTful API. 背景:我继承了一个旧数据库,可在其上构建新的RESTful API。 We're using Elide with Spring Boot to provide a JSON API compliant service. 我们将Elide与Spring Boot结合使用,以提供符合JSON API的服务。

Reference: Example source code 参考: 示例源代码

Problem: We have entities with a many-to-many relationship to each other and themselves by way of a join table. 问题:我们有一个实体,它们之间通过联接表相互之间以及彼此之间具有多对多关系。 Consider the followig schema: 考虑以下模式:

CREATE TABLE ALPHA (
  ID VARCHAR(255),
  NAME VARCHAR(255),
  CONSTRAINT PK_ALPHA PRIMARY KEY (ID)
);

CREATE TABLE BRAVO (
  ID VARCHAR(255),
  NAME VARCHAR(255),
  CONSTRAINT PK_BRAVO PRIMARY KEY (ID)
);

CREATE TABLE RELATIONSHIP (
  ID INT AUTO_INCREMENT,
  FROM_ID VARCHAR(255),
  TO_ID VARCHAR(255)
);

Where the resource entities are modeled as follows: 资源实体的建模如下:

public class Alpha implements Serializable {

    private String id;
    private String name;
    private Set<Alpha> alphas = new HashSet<>();
    private Set<Bravo> bravos = new HashSet<>();

    @Id
    @Column(name = "ID", unique = true, nullable = false)
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid")
    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
            name = "RELATIONSHIP",
            joinColumns = @JoinColumn(name = "FROM_ID"),
            inverseJoinColumns = @JoinColumn(name = "TO_ID")
    )
    public Set<Alpha> getAlphas() {
        return alphas;
    }

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
            name = "RELATIONSHIP",
            joinColumns = @JoinColumn(name = "FROM_ID"),
            inverseJoinColumns = @JoinColumn(name = "TO_ID")
    )
    public Set<Bravo> getBravos() {
        return bravos;
    }

}

And the relationship table: 和关系表:

public class Relationship implements Serializable {

    private Integer id;
    private String fromId;
    private String toId;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

    @Column(name = "FROM_ID")
    public String getFromId() {
        return fromId;
    }

    @Column(name = "TO_ID")
    public String getToId() {
        return toId;
    }

}

Now let's say we have an Alpha record A1 with relationships to A2 , A3 , B1 , and B2 . 现在,我们有一个Alpha记录A1 ,它与A2A3B1B2有关系。 First we delete the relationship to A2 . 首先,我们删除与A2的关系。

From our API this would be a DELETE request to http://localhost:9000/api/alphas/a1/relationships/alphas with BODY 从我们的API中,这将是对带有BODY的http://localhost:9000/api/alphas/a1/relationships/alphasDELETE请求

{
  "data": [
    {
      "type": "alphas", 
      "id": "a2" 
    }
  ]
}

Behind the scenes Hibernates does what I'm expecting and generates the following SQL queries: Hibernates在后台执行我所期望的操作,并生成以下SQL查询:

2018-07-13 09:48:23.687 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL                        : 
Hibernate: 
    select
        alpha0_.id as id1_0_,
        alpha0_.name as name2_0_ 
    from
        alpha alpha0_ 
    where
        alpha0_.id in (
            ?
        )
2018-07-13 09:48:23.688 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.690 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL                        : 
Hibernate: 
    select
        alphas0_.from_id as from_id2_2_0_,
        alphas0_.to_id as to_id3_2_0_,
        alpha1_.id as id1_0_1_,
        alpha1_.name as name2_0_1_ 
    from
        relationship alphas0_ 
    inner join
        alpha alpha1_ 
            on alphas0_.to_id=alpha1_.id 
    where
        alphas0_.from_id=?
2018-07-13 09:48:23.690 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.699 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL                        : 
Hibernate: 
    select
        alpha0_.id as id1_0_,
        alpha0_.name as name2_0_ 
    from
        alpha alpha0_ 
    where
        alpha0_.id in (
            ?
        )
2018-07-13 09:48:23.699 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a2]
2018-07-13 09:48:23.721 DEBUG 7964 --- [nio-9000-exec-5] org.hibernate.SQL                        : 
Hibernate: 
    delete 
    from
        relationship 
    where
        from_id=? 
        and to_id=?
2018-07-13 09:48:23.722 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [a1]
2018-07-13 09:48:23.724 TRACE 7964 --- [nio-9000-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [a2]

The key piece being delete from relationship where from_id=? and to_id=? delete from relationship where from_id=? and to_id=?的关键片段delete from relationship where from_id=? and to_id=? delete from relationship where from_id=? and to_id=?

Now the problem arises when trying to delete the second Alpha relationship A3 , in which Hibernate does almost the exact same sequence, except for the DELETE query which omits the and to_id=? 现在,当尝试删除第二个Alpha关系A3 ,就会出现问题,其中Hibernate几乎执行完全相同的序列,除了DELETE查询省略了and to_id=? from the query, ie 从查询,即

Hibernate: 
    delete 
    from
        relationship 
    where
        from_id=?

Which has the unintended consequence of deleting all other A1 relationships in the table, ie B1 and B2 . 这具有删除表中所有其他A1关系(即B1B2的意外结果。

So that is the crux of my problem. 这就是我问题的症结所在。 It seems like Hibernate is only seeing one other related Alpha record and therefore deciding to simplify the query by omitting the and to_id statement. 似乎Hibernate只能看到另一条相关的Alpha记录,因此决定通过省略and to_id语句来简化查询。

I'm probably missing something terribly obvious! 我可能错过了非常明显的东西!

I should also point out that I attempted to use a composite key on the relationship table but to no avail. 我还应该指出,我尝试在relationship表上使用复合键,但无济于事。

This is an unusual design, which I suspect is confusing Hibernate . 这是一个不寻常的设计,我怀疑它会使Hibernate感到困惑。 Sharing a single join table between multiple Many-to-many relationships, isn't good database design, for one it can't have any foreign keys/referential integrity. 在多个Many-to-many关系之间共享一个联接表不是一个好的数据库设计,因为它不能具有任何外键/参照完整性。

Secondly, Hibernate manages relationships, and therefore has control over the @JoinTable , I don't know how it would handle multiple entity relationships mapped with the same table. 其次, Hibernate管理关系,因此可以控制@JoinTable ,我不知道它如何处理映射到同一表的多个实体关系。 Evidently, not very well though! 显然,不是很好!

The simplest solution (if you're able to), would be to have 2 mapping tables. 最简单的解决方案(如果可以的话)是拥有2个映射表。 One for the relationship between Alpha-Alpha and another between Alpha-Bravo . 一个用于Alpha-Alpha之间的关系,另一个用于Alpha-Bravo之间的关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM