使用findBy时如何避免不必要Flush引发的瞬态实例异常
Hibernate在事务上下文执行查询方法(比如你用的findByCodelistCodeAndCode)时,默认会触发自动Flush。这么做是为了保证查询结果能反映当前会话里所有未提交的修改,避免数据不一致。
你的场景里,你给新创建的CodelistItemField关联了一个仅含code的临时CodelistItemFieldDefinition对象——这个临时对象是**瞬态(Transient)**的,既没被Hibernate会话管理,也没有持久化的ID。当Hibernate自动Flush时,会检查所有关联实体的状态,发现持久化状态的CodelistItemField引用了未保存的瞬态对象,就会抛出TransientPropertyValueException,这是Hibernate的默认校验逻辑,防止无效的实体关联被写入数据库。
1. 先查真实Definition再关联(优先推荐)
不要先创建临时Definition对象,直接用已知的code查询出数据库中已存在的CodelistItemFieldDefinition实例,再关联到新的CodelistItemField上:
// 直接从业务参数中获取code,避免从临时对象取值 String codelistCode = codelistItem.getCodelist().getCode(); String fieldDefCode = "你的字段定义code"; // 查询已持久化的真实Definition CodelistItemFieldDefinition realDefinition = codelistItemFieldDefinitionRepository.findByCodelistCodeAndCode(codelistCode, fieldDefCode); // 创建新字段并关联真实Definition CodelistItemField newField = new CodelistItemField(); newField.setCodelistItemFieldDefinition(realDefinition); // 设置其他字段... codelistItem.getCodelistItemFields().add(newField);
这种方式从根源上避免了瞬态对象的引入,完全符合Hibernate的状态管理规则,不会触发异常。
2. 将临时Definition附加到会话(备选方案)
如果业务逻辑必须先创建临时对象,可以通过EntityManager.merge()把临时对象转为受Hibernate管理的持久化状态对象(前提是数据库中确实存在对应code的记录):
// 创建临时Definition对象 CodelistItemFieldDefinition tempDef = new CodelistItemFieldDefinition(); tempDef.setCode("目标code"); // 合并到会话,Hibernate会自动匹配数据库中的对应记录 CodelistItemFieldDefinition managedDef = entityManager.merge(tempDef); // 用托管对象关联到新字段 newField.setCodelistItemFieldDefinition(managedDef);
注意:这个方式依赖code的唯一性,如果code不是唯一键,可能出现匹配错误,所以仅在特殊场景下使用。
3. 禁用查询前的自动Flush(谨慎使用)
可以给查询方法添加@QueryHints注解,指定Flush模式为COMMIT,让Hibernate只在事务提交时才触发Flush,而不是在查询前:
@Repository public interface CodelistItemFieldDefinitionRepository extends JpaRepository<CodelistItemFieldDefinition, Long> { @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.FLUSH_MODE, value = "COMMIT")) CodelistItemFieldDefinition findByCodelistCodeAndCode(String codelistCode, String code); }
⚠️ 注意:这个设置会让查询无法获取当前会话中未提交的修改,如果你的查询依赖这些修改,会导致数据不一致,所以仅在确认查询不需要依赖未提交数据时使用。
内容的提问来源于stack exchange,提问作者bruno grznar

