具有双向引用的 Hibernate 执行顺序
Hibernate execution order with bidirectional references
设置
我的环境是 Spring Boot 2.1 with MS SQL Server 和 Hibernate。我的设置包含一个具有特殊功能的简单 parent-child-association。一个parent有很多children。一个 parent 可能有一个最喜欢的 child。 parent 包含对收藏夹 child.
的引用
我对数据库设计没有影响。数据库模式:
PARENT
------------------------------------------------------------------------------------
ID NOT NULL,
FAVOURITE_CHILD_ID NULL,
CONSTRAINT FK_PARENT_FAVOURITE_CHILD_ID FOREIGN KEY(FAVOURITE_CHILD_ID) ON CHILD(ID)
CHILD
------------------------------------------------------------------------------------
ID NOT NULL,
PARENT_ID NOT NULL,
CONSTRAINT FK_CHILD_PARENT_ID FOREIGN KEY(PARENT_ID) ON PARENT(ID)
机型(简码):
@Entity
public class Parent {
@Id
@GeneratedValue(...)
@GenericGenerator(...)
@Column(name = "ID", updatable = false, nullable = false, unique = true)
private Long id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
private Set<Child> children;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "FAVOURITE_CHILD_ID", insertable = false, updatable = false)
private Child favourite;
}
@Entity
public class Child {
@Id
@GeneratedValue(...)
@GenericGenerator(...)
@Column(name = "ID", updatable = false, nullable = false, unique = true)
private Long id;
@Column(name = "PARENT_ID")
@NotNull
private Long parentId;
@ManyToOne(optional = false)
@JoinColumn(name = "PARENT_ID", insertable = false, updatable = false, nullable = false)
private Parent parent;
}
parent配置为级联删除其children.
存储库:
public interface ParentRepository extends CrudRepository<Parent, Long> {
}
pom.xml 引用了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
在启用 Spring 数据 REST 的情况下,应用程序创建一个 HTTP 端点服务于 parent 秒的请求。
问题
当我删除具有收藏夹 child 的 parent 时出现错误。示例数据包含 parent #1 没有最喜欢的 child 和 parent #2 有最喜欢的 child:
PARENT
ID FAVOURITE_CHILD_ID
-- ------------------
1 NULL
2 202
CHILD
ID PARENT_ID
--- ---------
101 1
102 1
201 2
202 2
http://localhost:8080/parents/1
上的请求 DELETE
运行良好并删除了第一个 parent 及其 children。
在 http://localhost:8080/parents/2
上请求 DELETE
引发异常。 SQL 服务器的消息是:
The DELETE statement conflicted with the REFERENCE constraint "FK_PARENT_FAVOURITE_CHILD_ID". The conflict occurred in database "test", table "dbo.PARENT", column 'FAVOURITE_CHILD_ID'.
Profiler 显示 Hibernates 首先运行 CHILD table 的删除语句:
exec sp_executesql N'delete from child where id=@P0',N'@P0 bigint',2
go
我希望 Hibernate 首先删除对收藏夹 child 的引用,即 UPDATE PARENT SET FAVOURITE_CHILD_ID = NULL WHERE ID = 2
.
但事实并非如此。有没有办法配置 Hibernate 让它自己解决这个问题?
尝试解决方案
我更喜欢配置解决方案。因为我找不到任何东西,所以我试图通过用自定义控制器覆盖 Spring 的存储库 REST 端点来接管更多控制权:
@RepositoryRestController
public class RepositoryRestMethodOverrideController {
@DeleteMapping(path = "/parents/{parentId}")
@Transactional
public ResponseEntity<?> deleteParent(@PathVariable Long parentId) {
Parent parent = parentRepository.findById(parentId).orElseThrow(NotFoundException::new);
if (parent.getFavouriteChildId() != null) {
parent.setFavouriteChildId(null);
parent.setFavouriteChild(null);
parentRepository.save(parent);
}
parentRepository.delete(parent);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
遗憾的是,我看到了相同的结果(错误消息)。有没有办法告诉 Hibernate 在运行删除命令之前写入更新 (parentRepository.save(parent)
)?
您想将 child 的 ID 设置为 null
,但您禁止更改该列:
@JoinColumn(name = "FAVOURITE_CHILD_ID", insertable = false, updatable = false)
private Child favourite;
只需删除 updatable = false
.
设置
我的环境是 Spring Boot 2.1 with MS SQL Server 和 Hibernate。我的设置包含一个具有特殊功能的简单 parent-child-association。一个parent有很多children。一个 parent 可能有一个最喜欢的 child。 parent 包含对收藏夹 child.
的引用我对数据库设计没有影响。数据库模式:
PARENT
------------------------------------------------------------------------------------
ID NOT NULL,
FAVOURITE_CHILD_ID NULL,
CONSTRAINT FK_PARENT_FAVOURITE_CHILD_ID FOREIGN KEY(FAVOURITE_CHILD_ID) ON CHILD(ID)
CHILD
------------------------------------------------------------------------------------
ID NOT NULL,
PARENT_ID NOT NULL,
CONSTRAINT FK_CHILD_PARENT_ID FOREIGN KEY(PARENT_ID) ON PARENT(ID)
机型(简码):
@Entity
public class Parent {
@Id
@GeneratedValue(...)
@GenericGenerator(...)
@Column(name = "ID", updatable = false, nullable = false, unique = true)
private Long id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
private Set<Child> children;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "FAVOURITE_CHILD_ID", insertable = false, updatable = false)
private Child favourite;
}
@Entity
public class Child {
@Id
@GeneratedValue(...)
@GenericGenerator(...)
@Column(name = "ID", updatable = false, nullable = false, unique = true)
private Long id;
@Column(name = "PARENT_ID")
@NotNull
private Long parentId;
@ManyToOne(optional = false)
@JoinColumn(name = "PARENT_ID", insertable = false, updatable = false, nullable = false)
private Parent parent;
}
parent配置为级联删除其children.
存储库:
public interface ParentRepository extends CrudRepository<Parent, Long> {
}
pom.xml 引用了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
在启用 Spring 数据 REST 的情况下,应用程序创建一个 HTTP 端点服务于 parent 秒的请求。
问题
当我删除具有收藏夹 child 的 parent 时出现错误。示例数据包含 parent #1 没有最喜欢的 child 和 parent #2 有最喜欢的 child:
PARENT
ID FAVOURITE_CHILD_ID
-- ------------------
1 NULL
2 202
CHILD
ID PARENT_ID
--- ---------
101 1
102 1
201 2
202 2
http://localhost:8080/parents/1
上的请求 DELETE
运行良好并删除了第一个 parent 及其 children。
在 http://localhost:8080/parents/2
上请求 DELETE
引发异常。 SQL 服务器的消息是:
The DELETE statement conflicted with the REFERENCE constraint "FK_PARENT_FAVOURITE_CHILD_ID". The conflict occurred in database "test", table "dbo.PARENT", column 'FAVOURITE_CHILD_ID'.
Profiler 显示 Hibernates 首先运行 CHILD table 的删除语句:
exec sp_executesql N'delete from child where id=@P0',N'@P0 bigint',2
go
我希望 Hibernate 首先删除对收藏夹 child 的引用,即 UPDATE PARENT SET FAVOURITE_CHILD_ID = NULL WHERE ID = 2
.
但事实并非如此。有没有办法配置 Hibernate 让它自己解决这个问题?
尝试解决方案
我更喜欢配置解决方案。因为我找不到任何东西,所以我试图通过用自定义控制器覆盖 Spring 的存储库 REST 端点来接管更多控制权:
@RepositoryRestController
public class RepositoryRestMethodOverrideController {
@DeleteMapping(path = "/parents/{parentId}")
@Transactional
public ResponseEntity<?> deleteParent(@PathVariable Long parentId) {
Parent parent = parentRepository.findById(parentId).orElseThrow(NotFoundException::new);
if (parent.getFavouriteChildId() != null) {
parent.setFavouriteChildId(null);
parent.setFavouriteChild(null);
parentRepository.save(parent);
}
parentRepository.delete(parent);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
遗憾的是,我看到了相同的结果(错误消息)。有没有办法告诉 Hibernate 在运行删除命令之前写入更新 (parentRepository.save(parent)
)?
您想将 child 的 ID 设置为 null
,但您禁止更改该列:
@JoinColumn(name = "FAVOURITE_CHILD_ID", insertable = false, updatable = false)
private Child favourite;
只需删除 updatable = false
.