传播级联删除引发外键约束失败

Propagate cascade delete raises foreign key constraint fails

我正在使用 Spring Boot (2.1.0.RELEASE) 和 Spring Data JPA。数据库是 MySQL.

我在链式级联删除时遇到了一些问题。

我有以下型号:

我不使用 @ManyToMany 因为我需要在生成的表中添加额外的字段,所以我的实体如下(无用的属性已被删除):

@Audited
@Entity
@Table(name = "request")
public class Request {

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private Resource resource;

}


@Audited
@Entity
@Table(name = "resource")
public class Resource {

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "resource")
    private Set<ResourceArticle> resourceArticles;

}

@Audited
@Entity
@Table(name = "resource_article")
public class ResourceArticle {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "article_id")
    private Article article;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "resource_id")
    private Resource resource;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "resourceArticle")
    private Set<ResourceArticleOption> options;

}

@Audited
@Entity
@Table(name = "resource_article_option")
public class ResourceArticleOption {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "option_id")
    private Option option;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "resource_article_id")
    private ResourceArticle resourceArticle;

}

然后我的经理发出请求 delete 扩展 CrudRepository :

/* Repositroy */
public interface RequestRepository extends CrudRepository<Request, Long> {
}

/* Manager */
@Transactional
@Component("requestMgr")
public class RequestManager {

    @Autowired
    RequestRepository requestRepository;

    public void delete(Request request) {
        requestRepository.delete(request);
    }

}

/* Viewmodel */
public class RequestVm {

    @WireVariable
    private RequestManager requestMgr;

    public void deleteRequest(Request req) {
        requestMgr.delete(req);
    }

}

错误是:

Caused by: java.sql.SQLException: Cannot delete or update a parent row: a foreign key constraint fails (my_db.resource_article, CONSTRAINT FK5wqvprkwx05fb5hgt6w9h7nbk FOREIGN KEY (resource_id) REFERENCES resource (id))

启用跟踪时的输出:

delete from request where id=?
binding parameter [1] as [BIGINT] - [24]
delete from resource where id=?
binding parameter [1] as [BIGINT] - [71]
SQL Error: 1451, SQLState: 23000
(conn=30) Cannot delete or update a parent row: a foreign key constraint fails (`my_db`.`resource_article`, CONSTRAINT `FK5wqvprkwx05fb5hgt6w9h7nbk` FOREIGN KEY (`resource_id`) REFERENCES `resource` (`id`))

奇怪的是它试图按顺序删除 request > resource > resource_article > resource_article_option ?

需要 CascadeType.ALL 因为我想坚持并删除。

我可以通过在删除前设置对 null 的引用来打破链条,但这当然会导致数据库中出现孤立记录。

这里最好的策略是什么?

我通过在每个关系中删除不是关系所有者的实体来使其工作。

@ManyToMany 关系中的级联删除不仅适用于 link table,也适用于关系的另一端。

@ManyToMany关联中删除时是一个well-known问题(这很好解释here例如),但我没有注意,因为我的注释不直接@ManyToMany 但是双 @OneToMany 关联,我虽然 JPA 会像在简单的 @OneToMany 关系中一样级联删除,但看来我错了。

在删除请求之前,我已经使用 @preRemove 清理了关系:

ResourceArticle中:

@PreRemove
public void preRemove() {
    article.getResourceArticles().remove(this);
}

ResourceArticleOption中:

@PreRemove
public void preRemove() {
    option.getResourceArticleOptions().remove(this);
}

然后一切正常。