EclipseLink与Hibernate持久化上下文行为差异问题咨询
问题成因及解决方案
核心差异原因
这个问题是两种JPA实现对@Transactional(readOnly = true)只读事务的脏数据处理策略不同导致的:
- Hibernate对只读事务做了针对性优化:开启只读事务后,加载的实体会被标记为只读状态,脏跟踪机制会跳过这类实体的变更检测,事务提交时也不会执行flush操作,你对托管态实体的修改仅会停留在当前事务的一级缓存中,不会同步到数据库。
- EclipseLink的默认策略和Hibernate不同:即便在只读事务中,只要你修改了托管态的实体属性,事务提交时依然会触发flush操作,把变更直接同步到数据库,这就是你没有调用persist也会自动持久化数据的原因。你配置中注释掉的
eclipselink-persistence-context.persist-on-commit: false就是控制这个行为的参数。
修复方案
方案1:调整EclipseLink配置(推荐)
在application-dev.yml的JPA配置中取消对应参数的注释,关闭只读事务下的自动持久化行为:
spring: jpa: properties: eclipselink.persistence-context.persist-on-commit: false eclipselink.transaction.join-existing: true
配置完成后EclipseLink的行为就会和Hibernate对齐,只读事务下的实体变更不会同步到数据库。
方案2:业务代码层面规避
如果不想修改全局配置,可以在修改实体前先将其从持久化上下文中剥离,变成游离态后再修改,变更就不会被跟踪:
d.ifPresent(user -> { entityManager.detach(user); user.setDepartmentName("test1234"); });
注意要先注入EntityManager对象再调用detach方法。
方案3:避免修改托管态实体
如果你只是需要返回修改后的值给前端,不要直接修改查询得到的托管实体,而是拷贝为DTO或者新的实体对象后再修改属性,从根源上避免脏数据问题。
内容的提问来源于stack exchange,提问作者Trainer
相关产品推荐
相关产品推荐

