传播级联删除引发外键约束失败
Posted
技术标签:
【中文标题】传播级联删除引发外键约束失败【英文标题】:Propagate cascade delete raises foreign key constraint fails 【发布时间】:2019-10-15 05:36:54 【问题描述】:我将 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);
错误是:
原因:java.sql.SQLException:无法删除或更新父级 行:外键约束失败 (
my_db
.resource_article
, 约束FK5wqvprkwx05fb5hgt6w9h7nbk
外键 (resource_id
) 参考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
的引用来打破链条,但当然会导致数据库中出现孤立记录。
这里最好的策略是什么?
【问题讨论】:
请注意,您可以级联多个操作而无需级联所有操作。这并不一定意味着您不需要或不需要CascadeType.ALL
,或者使用它会导致您的问题,只是您给出使用它的原因无效(JPA 识别的操作不仅仅是PERSIST
和REMOVE
).
据我所知或所知,删除 Resource
via 扩展 CrudRepository
的存储库应遵守为父实体声明的 JPA 级联属性。这似乎没有发生,这让我怀疑有些东西不是你所呈现的那样,或者有些重要的东西被遗漏了。也许您的实体的部署版本没有启用级联?也许您已经自定义了您的存储库,使其 delete()
方法以某种方式绕过 JPA?
谢谢约翰。确实我说错了,我知道我可以使用多种级联类型,但据我所知,我没有理由不级联所有东西,所以我使用ALL
。我在这里没有介绍的其他属性是基本的@Column
,应该不会造成任何问题。我自愿省略的唯一一件事是我在此之上还有另一个表,REQUEST
表与RESOURCE
具有 1-1 关系,删除是根据此请求完成的。最后请注意,我使用 Envers 进行审计,我的实体有 @Audited
注释,所以对于每个表我都有另一个 xxx_audit_log
表。我已经更新了我的帖子。
【参考方案1】:
我通过在每个关系中删除不是关系所有者的实体来使其工作。
@ManyToMany
关系中的级联删除不仅适用于链接表,还适用于关系的另一端。
从@ManyToMany
关联中删除时是众所周知的问题(这很好解释here例如),但我没有注意,因为我的注释不是直接@ManyToMany
而是双重@OneToMany
关联, 我虽然 JPA 会像简单的@OneToMany
关系那样级联删除,但似乎我错了。
在删除请求之前,我使用@preRemove
清理了关系:
在ResourceArticle
:
@PreRemove
public void preRemove()
article.getResourceArticles().remove(this);
在ResourceArticleOption
:
@PreRemove
public void preRemove()
option.getResourceArticleOptions().remove(this);
然后一切正常。
【讨论】:
以上是关于传播级联删除引发外键约束失败的主要内容,如果未能解决你的问题,请参考以下文章