【问题标题】:Hibernate execution order with bidirectional references具有双向引用的休眠执行顺序
【发布时间】:2020-08-23 03:41:42
【问题描述】:

设置

我的环境是带有 MS SQL Server 和 Hibernate 的 Spring Boot 2.1。我的设置包含一个具有特殊功能的简单父子关联。一个父母有很多孩子。父母可能有一个最喜欢的孩子。父母持有对最喜欢的孩子的引用。

对数据库设计没有影响。数据库架构:

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

父级配置为级联删除其子级。

存储库:

public interface ParentRepository extends CrudRepository<Parent, Long> {
}

pom.xml 有一个引用:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

启用 Spring Data REST 后,应用程序会创建一个 HTTP 端点,为父母提供服务。

问题

当我删除有喜欢的孩子的父母时,我收到错误消息。示例数据包含没有最喜欢的孩子的父母 #1 和有最喜欢的孩子的父母 #2:

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 运行良好,并删除了第一个父级及其子级。

http://localhost:8080/parents/2 上请求DELETE 会引发异常。 SQL Server 的消息是:

DELETE 语句与 REFERENCE 约束“FK_PARENT_FAVOURITE_CHILD_ID”冲突。冲突发生在数据库“test”、表“dbo.PARENT”、列“FAVOURITE_CHILD_ID”中。

Profiler 显示 Hibernates 首先运行 CHILD 表的删除语句:

exec sp_executesql N'delete from child where id=@P0',N'@P0 bigint',2
go

我希望 Hibernate 首先删除对最喜欢的孩子的引用,即对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))?

【问题讨论】:

  • 应该不惜一切代价避免循环依赖。为什么不禁用或删除 Forienn 键?你到底想要什么?
  • 正如我所写的,我对数据库模式没有影响。我想知道如何配置(或使用)Hibernate 以便能够删除有喜欢的孩子的父母。

标签: java sql-server spring spring-boot hibernate-mapping


【解决方案1】:

您想将孩子的 id 设置为null,但您已禁止更改列:

@JoinColumn(name = "FAVOURITE_CHILD_ID", insertable = false, updatable = false)
private Child favourite;

只需删除updatable = false

【讨论】:

  • 糟糕...现在它也可以在没有额外控制器的情况下工作。感谢您校对我的代码!
猜你喜欢
  • 2016-12-28
  • 2017-10-06
  • 2012-10-14
  • 2015-10-14
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多