spring-boot - Spring Data problem - derived delete doesn't work
问题描述
I have a spring boot application (based off spring-boot-starter-data-jpa. I have an absolute minimum of configuration going on, and only a single table and entity.
I'm using CrudRepository<Long, MyEntity> with a couple of findBy methods which all work. And I have a derived deleteBy method - which doesn't work. The signature is simply:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
Long deleteBySystemId(String systemId);
// findBy methods left out
}
The entity is simple, too:
@Entity @Table(name="MyEntityTable")
public class MyEntity {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="MyEntityPID")
private Long MyEntityPID;
@Column(name="SystemId")
private String systemId;
@Column(name="PersonIdentifier")
private String personIdentifier;
// Getters and setters here, also hashCode & equals.
}
The reason the deleteBy method isn't working is because it seems to only issue a "select" statement to the database, which selects all the MyEntity rows which has a SystemId with the value I specify. Using my mysql global log I have captured the actual, physical sql and issued it manually on the database, and verified that it returns a large number of rows.
So Spring, or rather Hibernate, is trying to select the rows it has to delete, but it never actually issues a DELETE FROM statement.
According to a note on Baeldung this select statement is normal, in the sense that Hibernate will first select all rows that it intends to delete, then issue delete statements for each of them.
Does anyone know why this derived deleteBy method would not be working? I have @TransactionManagementEnabled on my @Configuration, and the method calling is @Transactional. The mysql log shows that spring sets autocommit=0 so it seems like transactions are properly enabled.
I have worked around this issue by manually annotating the derived delete method this way:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
@Modifying
@Query("DELETE FROM MyEntity m where m.systemId=:systemId")
Long deleteBySystemId(@Param("systemId") String systemId);
// findBy methods left out
}
This works. Including transactions. But this just shouldn't have to be, I shouldn't need to add that Query annotation.
Here is a person who has the exact same problem as I do. However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
Oh, for reference I'm using Spring Boot 2.2.9.
解决方案
tl;博士
这一切都在参考文档中。这就是 JPA 的工作方式。(我搓手洗手。)
细节
这两种方法做了两件不同的事情:Long deleteBySystemId(String systemId);
通过给定的约束加载实体并最终发布EntityManager.delete(…)
持久性提供者将延迟到事务提交之前的发布。即,该调用之后的代码不能保证更改已经同步到数据库。这反过来又是由于 JPA 允许其实现真正做到这一点。不幸的是,Spring Data 无法解决此问题。(更多的摩擦,更多的洗涤,加上一点肥皂。)
参考文档证明了这种行为需要EntityManager
(同样是 JPA 抽象,与 Spring Data 无关)来触发@PreDelete
用户期望触发的生命周期事件等。
手动声明修改查询的第二种方法是声明要在数据库中执行的查询,这意味着实体生命周期不会触发,因为实体没有预先实现。
然而,Spring 开发人员很快就洗手并将其作为 Hibernate 问题注销,因此在那里找不到解决方案或解释。
有详细的解释为什么它的工作方式在票的评论中工作。甚至提供了解决方案。解决方法和建议,通过控制此行为的堆栈部分提出此问题。(关上水龙头,伸手去拿毛巾。)
推荐阅读
- javascript - 将猫鼬文档嵌套到另一个集合文档
- python-3.x - 有没有办法检查所有变量是否包含一个变量
- configuration - WebLogic 桥消息:“Web 服务器桥失败:没有可用于连接的后端服务器...”
- microsoft-graph-api - 如何在不使用 App 令牌的情况下获取用户的邮箱设置时区?
- svg - 如何旋转svg的一部分?
- json - 如何提交应该在同一个 Vuex Store 对象中并且未映射到 store 对象的多个嵌套值?
- oracle - Sybase 到 Oracle 表的移动
- sql - 考虑到许多因素,我如何计算任务完成日期
- r - R for 循环返回 NULL 而不是预期值
- angular - 角度计算 ngfor 累积值和通过父级的总计