[英]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
,它与A2
, A3
, B1
和B2
有关系。 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/alphas
的DELETE
请求
{
"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
关系(即B1
和B2
的意外结果。
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.