java - JPA/hibernate : ManyToMany delete relation on Join Table

标签 java hibernate jpa

免责声明:这是我的第一个 Java 项目;边走边学。

背景:我继承了一个旧数据库,并在其上构建新的 RESTful API。我们正在使用Elide使用 Spring Boot 提供JSON API合规服务。

引用: Example source code

问题:我们的实体通过连接表相互之间以及它们自身之间存在多对多关系。考虑以下模式:

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

其中资源实体建模如下:

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

}

以及关系表:

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

}

现在假设我们有一个 Alpha记录A1A2 的关系, A3 , B1 ,和B2 。首先我们删除与A2的关系.

从我们的 API 来看,这将是 DELETE请求http://localhost:9000/api/alphas/a1/relationships/alphas与 body

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

Hibernate 在幕后执行我所期望的操作并生成以下 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]

关键部分是delete from relationship where from_id=? and to_id=?

现在尝试删除第二个 Alpha 时出现问题关系A3 ,其中 Hibernate 执行几乎完全相同的序列,除了 DELETE 之外。省略 and to_id=? 的查询从查询中,即

Hibernate: 
    delete 
    from
        relationship 
    where
        from_id=?

这会产生删除所有其他 A1 的意外后果表中的关系,即B1B2 .

这就是我问题的症结所在。看来 Hibernate 只看到了另一个相关的 Alpha记录,因此决定通过省略 and to_id 来简化查询声明。

我可能错过了一些非常明显的东西!

我还应该指出,我尝试在 relationship 上使用复合键表但无济于事。

最佳答案

这是一个不寻常的设计,我怀疑它混淆了 Hibernate。在多个多对多关系之间共享单个联接表并不是好的数据库设计,因为它不能具有任何外键/引用完整性。

其次,Hibernate 管理关系,因此可以控制 @JoinTable,我不知道它如何处理与同一个表映射的多个实体关系。显然,情况不太好!

最简单的解决方案(如果可以的话)是有 2 个映射表。一个是 Alpha-Alpha 之间的关系,另一个是 Alpha-Bravo 之间的关系。

关于java - JPA/hibernate : ManyToMany delete relation on Join Table,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51329827/

相关文章:

java - 将项目添加到工作区文件夹后,Eclipse 将不会更新

java - setText 不向 TextView 显示值

java - hibernate 5.2.17 : ORA-01797: this operator must be followed by ANY or ALL

java - 手指数据匹配错误

java - 如何从 json rest 服务器答案中检索字段?

java - 实体映射正在动态创建不需要的列

java - Hibernate 尝试获取用户时出现 Spring 安全错误

java - 在 Hibernate 中连接不同类型的列

java - JPA 的@UniqueConstraint 注释似乎不能保证唯一性

java - 类似于 HQL 中的 % 运算符