内存H2数据库删除实体后EntityManager.find仍返回该实体的问题
问题原因分析
这是Hibernate一级缓存(持久化上下文)的特性导致的:
EntityManager.find()方法会优先从**一级缓存(当前EntityManager实例维护的内存缓存)**中查找实体,只有缓存中不存在时才会去数据库查询。- 你使用JPQL的
delete语句执行删除操作时,该操作会直接修改数据库中的数据,但不会同步更新EntityManager的一级缓存——JPQL批量操作绕过了持久化上下文的实体管理机制,缓存中依然保留着之前加载的实体对象。
在你的测试流程中:
- 第一次
persist操作将实体存入了一级缓存; - 后续的
findByReportId1直接从缓存中获取实体,验证通过; - 调用
deleteByReportId执行JPQL删除,仅删除了数据库中的记录,但缓存里的实体并未被清除或标记为删除; - 事务提交后再次调用
find方法,EntityManager依然从缓存中返回旧的实体对象,导致断言失败。
而DAO的getByReportId使用JPQL查询,该查询会直接从数据库获取最新数据,因此能正确返回空结果。
解决方案
可以通过以下几种方式解决这个问题:
1. 清除EntityManager的一级缓存
在删除操作提交事务后,调用em.clear()清除缓存,这样后续的find方法会强制去数据库查询:
tx.commit(); em.clear(); // 清除一级缓存 tx.begin(); var getByReportId2 = blobInfoDao.getByReportId(reportId); assertFalse(getByReportId2.isPresent()); BlobInfoDBVO findByReportId2 = em.find(BlobInfoDBVO.class, reportId); assertNull(findByReportId2); // 现在会通过
2. 使用EntityManager.remove()删除实体
放弃JPQL批量删除,改为先通过find获取实体,再调用remove方法删除,这样Hibernate会同步更新一级缓存:
// 修改DAO的deleteByReportId方法 public void deleteByReportId(long reportId) { BlobInfoDBVO blobInfo = em.find(BlobInfoDBVO.class, reportId); if (blobInfo != null) { em.remove(blobInfo); } }
这种方式会同时删除数据库记录并清除缓存中的实体,后续find方法会返回null。
3. 强制find方法从数据库刷新数据
调用find时指定刷新模式,强制绕过缓存直接查询数据库:
BlobInfoDBVO findByReportId2 = em.find(BlobInfoDBVO.class, reportId, LockModeType.READ); // 或者设置EntityManager的刷新模式 em.setFlushMode(FlushModeType.COMMIT); BlobInfoDBVO findByReportId2 = em.find(BlobInfoDBVO.class, reportId);
内容的提问来源于stack exchange,提问作者zolv
相关产品推荐
相关产品推荐

