JPA持久化上下文机制疑问:事务内delete后查询为何返回null
问题根本原因
你对delete()操作的持久化上下文行为认知存在偏差,这是返回null的核心原因:
- JPA调用实体删除方法后,不会直接将实体从持久化上下文(PC/一级缓存)中移除,而是会将实体标记为
REMOVED(待删除)状态,同时将删除操作加入待执行的SQL队列,默认在事务提交前才会flush到数据库。 - 当你在同一事务内调用
findById查询同ID实体时,JPA实现(默认Hibernate)会优先检查持久化上下文中的实体状态:如果对应ID的实体已经被标记为REMOVED,会直接返回null,不会发送查询SQL到数据库。
完整执行逻辑拆解
对应你的测试代码,整个事务内的执行流程如下:
- 新建
Member实例调用save()后,实体进入持久化上下文变为托管状态,此时调用findById会直接从PC中返回同个实例,所以member == actual结果为true。 - 修改托管实体的
username字段后,持久化上下文会自动检测到脏数据,不需要主动调用save()也会在后续flush时同步到数据库,此时findById仍然返回PC中的托管实例,所以用户名对比结果为true。 - 调用
delete(actual)后,该实体被标记为REMOVED状态,仍然存在于持久化上下文中,删除SQL暂未发送到数据库。 - 再次调用
findById时,Hibernate检测到PC中同ID实体处于待删除状态,直接返回null,无数据库查询动作。
验证方法
如果要验证数据库中此时确实还存在该数据,可以在删除操作后、查询操作前,调用entityManager.detach(actual)将待删除实体从持久化上下文中剥离,此时再调用findById就会发送SQL到数据库查询,能够拿到未被删除的实体数据。
内容的提问来源于stack exchange,提问作者Rebwon
相关产品推荐
相关产品推荐

