Spring boot delete 没有级联,它将外键设置为 null,然后在 null 约束上失败

Posted

技术标签:

【中文标题】Spring boot delete 没有级联,它将外键设置为 null,然后在 null 约束上失败【英文标题】:Spring boot delete is not cascading it is setting the foriegn key to null and then failing on a null constraint 【发布时间】:2020-02-28 04:14:32 【问题描述】:

在 Spring Boot 中使用 deleteById 和一对多关系,生成的查询会尝试在引用的实体中将外键设置为 null 而不是删除它们。我正在使用默认存储库 deleteById

我已经在实体中的外键定义上将 Cascadetype 设置为 ALL 并将 OrpahnRemoval 设置为 true,并且我在创建表的 DDL 中设置了 ON DELETE CASCADE。

这里是控制器类中的删除操作

@Transactional
@DeleteMapping("transferImage/imageId")
public void  deleteTransferImage(@PathVariable int imageId) 
    repository.deleteById(imageId);

这里是从父实体到子实体的引用

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
@JoinColumn(name = "TRANSFER_IMAGE_ID")
private List<TransferPartition> partitions = new ArrayList<>();

这里是子实体中外键的定义

@JsonIgnore
@ManyToOne
@JoinColumn(name = "TRANSFER_IMAGE_ID", referencedColumnName = "TRANSFER_IMAGE_ID")
private TransferImage image;

这是用于创建两个表的 DDL

CREATE TABLE TRANSFER_IMAGE (
TRANSFER_IMAGE_ID       SERIAL CONSTRAINT TRANSFER_IMAGE_PK PRIMARY KEY,
IMAGE_NAME              VARCHAR(50) CONSTRAINT TRANSFER_IMAGE_NAME_UK UNIQUE NOT NULL ,
REQUESTED_PART_SIZE_MB  INTEGER NOT NULL,
SIZE_BYTES              INTEGER NOT NULL,
IMAGE_MD5_HASH          VARCHAR(100),
NUMBER_PARTITIONS       INTEGER,
DELETED                 BOOLEAN  NOT NULL
);

CREATE TABLE TRANSFER_PARTITION (
TRANSFER_PARTITION_ID   SERIAL CONSTRAINT TRANSFER_PARTITION_PK PRIMARY KEY,
TRANSFER_IMAGE_ID       INTEGER NOT NULL CONSTRAINT TRANSFER_PARTITION_IMAGE_FK REFERENCES TRANSFER_IMAGE ON DELETE CASCADE ON UPDATE CASCADE,
PARTITION_NUMBER        INTEGER NOT NULL,
PARTITION_MD5_HASH      VARCHAR(100) NOT NULL,
SIZE_BYTES              INTEGER NOT NULL
);

这是出现在日志中的查询

 Hibernate:
  select
    transferim0_.transfer_image_id as transfer1_13_0_,
    transferim0_.deleted as deleted2_13_0_,
    transferim0_.image_md5_hash as image_md3_13_0_,
    transferim0_.image_name as image_na4_13_0_,
    transferim0_.number_partitions as number_p5_13_0_,
    transferim0_.requested_part_size_mb as requeste6_13_0_,
    transferim0_.size_bytes as size_byt7_13_0_,
    partitions1_.transfer_image_id as transfer5_14_1_,
    partitions1_.transfer_partition_id as transfer1_14_1_,
    partitions1_.transfer_partition_id as transfer1_14_2_,
    partitions1_.transfer_image_id as transfer5_14_2_,
    partitions1_.partition_number as partitio2_14_2_,
    partitions1_.partition_md5_hash as partitio3_14_2_,
    partitions1_.size_bytes as size_byt4_14_2_
from
    transfer_image transferim0_
left outer join
   transfer_partition partitions1_
       on transferim0_.transfer_image_id=partitions1_.transfer_image_id
where
      transferim0_.transfer_image_id=?
Hibernate:
update
    transfer_partition
 set
     transfer_image_id=null
 where
     transfer_image_id=?

当我通过主键删除父级时,我期望删除引用父级 (TransferImage) 的所有子实体 (TransferPartition)。相反,我得到一个引用外键列的空约束错误。在我看来,生成的 SQL 将外键列设置为 null 而不是删除行。

ERROR: null value in column "transfer_image_id" violates not-null constraint
Detail: Failing row contains (1, null, 1, asdfaa1-1, 20000000).

如果我从 psql 提示符中删除 transfer_image 表中的图像,删除级联会正确并删除引用的分区。

delete  from transfer_image i where i.transfer_image_id = 1

【问题讨论】:

【参考方案1】:

您的问题在于 TransferImage 类中外键关系的定义。

代替

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
@JoinColumn(name = "TRANSFER_IMAGE_ID")
private List<TransferPartition> partitions = new ArrayList<>();

你应该使用

@OneToMany(mappedBy="image", cascade = CascadeType.ALL, fetch = FetchType.EAGER, OrphanRemoval = true)
private List<TransferPartition> partitions = new ArrayList<>();

另见https://www.baeldung.com/hibernate-one-to-many

【讨论】:

你成就了我的一天。谢谢。

以上是关于Spring boot delete 没有级联,它将外键设置为 null,然后在 null 约束上失败的主要内容,如果未能解决你的问题,请参考以下文章

Java Spring boot hibernate删除级联数据

on_delete=models.CASCADE级联删除

on_delete=models.CASCADE级联删除

Spring Boot + Spring Security 应用程序中 POST/PUT/DELETE 请求的 403 响应

Spring Boot Data Rest + CORS 未正确启用 OPTIONS/DELETE

spring boot 2.0 之后发送delete,put请求问题