为啥手动定义的 Spring Data JPA 删除查询不会触发级联?
Posted
技术标签:
【中文标题】为啥手动定义的 Spring Data JPA 删除查询不会触发级联?【英文标题】:Why does a manually defined Spring Data JPA delete query not trigger cascades?为什么手动定义的 Spring Data JPA 删除查询不会触发级联? 【发布时间】:2014-06-20 00:37:47 【问题描述】:我有以下问题:当我尝试删除具有以下关系的实体时:
@OneToMany(mappedBy="pricingScheme", cascade=CascadeType.ALL, orphanRemoval=true)
private Collection<ChargeableElement> chargeableElements;
通过提供的删除方法使用CrudRepository
,它会删除实体及其所有可收费元素,这很好。当我尝试使用自定义删除时出现问题:
@Modifying
@Query("DELETE FROM PricingScheme p WHERE p.listkeyId = :listkeyId")
void deleteByListkeyId(@Param("listkeyId") Integer listkeyId);
上面写着:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannot delete or update a parent row: a foreign key constraint fails
(`listkey`.`chargeableelements`, CONSTRAINT `FK_pox231t1sfhadv3vy7ahsc1wt`
FOREIGN KEY (`pricingScheme_id`) REFERENCES `pricingschemes` (`id`))
为什么不允许我这样做? @Query
方法不支持级联属性吗?我知道我可以先findByListkeyId(…)
,然后使用标准删除方法删除持久实体,但这并不优雅。是否可以按照我尝试的方式使用自定义 @Query
方法?
【问题讨论】:
【参考方案1】:这与 Spring Data JPA 无关,而是 JPA 指定它的工作方式(第 4.10 节 - “批量更新和删除操作”,JPA 2.0 规范):
删除操作仅适用于指定类及其子类的实体。它不会级联到相关实体。
如果您考虑一下,JPA 级联不是数据库级级联,而是由EntityManager
维护的级联。因此,EntityManager
需要知道要删除的实体实例及其相关实例。如果您触发查询,它实际上无法知道这些,因为持久性提供程序将其转换为 SQL 并执行它。因此,EntityManager
无法分析对象图,因为执行完全在数据库中进行。
可以在此处找到与此主题相关的问答over here。
【讨论】:
【参考方案2】:我遇到了同样的问题,为了解决这个问题,我在 @Modifying 注释中添加了属性 clearAutomatically 并具有真值。
@Modifying(clearAutomatically = true)
@Query("delete User u where u.id = :userId")
void deleteUserById(@Param("userId") Long userId);
除此之外,删除实体的服务方法使用 @Transaction 注释,插入用户的方法也是如此。
【讨论】:
clearAutomatically 与数据库级联无关。而是清除 entityManager 上下文。以上是关于为啥手动定义的 Spring Data JPA 删除查询不会触发级联?的主要内容,如果未能解决你的问题,请参考以下文章
spring-data-jpa循环保存数据,为啥只保存了最后一条数据
为啥在 Spring Data JPA Repository 上的 save() 之后使用返回的实例?
Spring data jpa repo,为啥需要接口服务和服务实现
如何使用 Spring Data JPA 保存具有手动分配标识符的实体?
Spring Data JPA 审计不适用于带有 @Modifying 注释的 JpaRepository 更新方法,为啥?