同一Hibernate Session中如何获取数据库表的更新值
你的问题核心是Hibernate Session的一级缓存(持久化上下文)导致内存对象与数据库不同步:当你通过命名查询直接更新数据库时,Hibernate并不会自动同步Session缓存中已加载的Result对象,所以内存里的instance还保留着更新前的旧值。
不用新开Session也能解决这个问题,这里有几个更优的实现方式:
1. 手动刷新缓存中的实体对象
这是最直接的方案:在执行完query.executeUpdate()之后,让Hibernate从数据库重新加载resultInstance的最新状态,同步到缓存里。修改你的updateResultStepsSequence方法即可:
private void updateResultStepsSequence(long resultId, Result resultInstance) { if (resultInstance.getStepCount() > 0) { List<ResultStep> resultSteps = resultInstance.getResultSteps(); if (resultSteps != null && !resultSteps.isEmpty() && (resultSteps.get(0).getDurationSum() == 0)) { try { Session session = SessionFactoryProvider.getSessionFactory().getCurrentSession(); Query query = session.getNamedQuery("updateResultStepSequence") .setLong("result_id", resultId); query.executeUpdate(); // 新增:刷新实体,同步数据库最新状态到内存对象 session.refresh(resultInstance); commit(); } catch (RuntimeException re) { ResultHome.log.error("Query execution failed", re); rollback(); throw re; } } } }
这样执行完更新后,resultInstance就会持有数据库的最新值,回到上层方法后返回的instance也是最新的了。
2. 改用ORM实体操作代替原生/命名查询
如果你的updateResultStepSequence命名查询只是做简单的属性更新,建议直接操作resultInstance关联的ResultStep对象,让Hibernate自动管理持久化和缓存同步:
private void updateResultStepsSequence(long resultId, Result resultInstance) { if (resultInstance.getStepCount() > 0) { List<ResultStep> resultSteps = resultInstance.getResultSteps(); if (resultSteps != null && !resultSteps.isEmpty() && (resultSteps.get(0).getDurationSum() == 0)) { try { Session session = SessionFactoryProvider.getSessionFactory().getCurrentSession(); // 直接修改ResultStep的属性,比如更新sequence字段 for (ResultStep step : resultSteps) { // 假设你的查询是更新sequence值,这里模拟设置新值 step.setSequence(/* 计算后的新值 */); } // 不需要手动executeUpdate,Hibernate会在commit时自动同步到数据库 commit(); } catch (RuntimeException re) { ResultHome.log.error("Update failed", re); rollback(); throw re; } } } }
这种方式完全符合ORM的设计思路,缓存和数据库天然保持一致,不需要额外处理缓存同步,适合简单的单实体/关联实体更新场景。
3. 清除缓存中的实体(可选)
如果之后不再需要使用旧的resultInstance,也可以调用session.evict(resultInstance)将其从缓存中移除,之后再重新通过get方法获取最新对象:
// 在executeUpdate之后 session.evict(resultInstance); // 重新获取最新对象 resultInstance = (Result) session.get("com.xxx.Result", resultId);
不过这种方式需要重新查询一次数据库,和你之前新开Session的思路类似,但不需要创建新Session,只是复用当前Session,适合不需要保留旧对象引用的场景。
内容的提问来源于stack exchange,提问作者MIM

