Spring Boot+JHipster项目Game表api_secret字段频繁被置为null排查求助
问题根因定位
你遇到的偶发api_secret被置空的问题核心来自JPA持久化上下文的实体状态跟踪机制:
从Spring Data JPA查询返回的Game实体在事务存续期间处于托管态,你在返回前手动将apiSecret字段设为null的操作会被Hibernate的持久化上下文自动跟踪。即便你没有显式调用save方法,只要事务提交前触发了flush操作,修改就会被同步到数据库。
你标注的@Transactional(readOnly = true)并未完全禁止flush的原因通常有两个:
readOnly=true仅会将事务默认FlushMode设为MANUAL,如果当前查询方法被外层的非只读事务嵌套调用,整个事务上下文的FlushMode会被覆盖为AUTO,事务提交时会自动刷入所有托管实体的修改- 你当前使用的Hibernate 5.4.29.Final存在少数只读事务意外flush的已知边缘问题,也可能触发修改同步
排查验证方案
- 打印当前事务属性验证传播场景:在所有修改
apiSecret的查询方法入口添加日志,输出TransactionAspectSupport.currentTransactionStatus().isReadOnly()的返回值,确认是否存在被外层非只读事务调用的情况 - 开启SQL与flush调试日志:添加如下配置定位update语句触发链路
所有spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true logging.level.org.hibernate.event.internal.DefaultFlushEventListener=DEBUGapi_secret的update语句触发时都会打印对应的调用栈,可直接定位到触发flush的业务方法 - 验证实体状态:修改
apiSecret前打印entityManager.contains(game)的返回值,如果返回true就说明实体处于托管态,修改会被跟踪
修复方案
优先推荐第一种方案,从根源避免问题:
- 采用DTO做返回值隔离:查询到
Game实体后转换为对应的DTO对象,仅将DTO的apiSecret字段设为null,完全不修改原始托管实体 - 托管实体修改前先脱管:如果一定要修改实体对象,修改前调用
entityManager.detach(game)将实体从持久化上下文移除,后续修改不会被跟踪 - 强制查询方法禁用flush:在所有查询方法上添加QueryHint强制不触发flush:
@QueryHints(value = @QueryHint(name = org.hibernate.annotations.QueryHints.FLUSH_MODE, value = "NEVER"))
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

