Spring Boot从2.6.7升级至2.7.2时触发TransientPropertyValueException
核心原因
Spring Boot 2.7.2 升级了依赖的 Hibernate 版本(至 5.6.x 系列),该版本对关联实体的状态校验逻辑变得更严格,同时 Spring Boot 2.7 默认启用了 hibernate.check_nullability=true 配置,会在持久化前严格检查关联实体的状态。
你的代码在 2.6.7 版本正常,主要因为旧版 Hibernate 对 detached(脱离当前会话)状态的实体校验更宽松,甚至会自动合并这类实体;而升级后,Hibernate 要求关联的非空实体必须处于托管(Managed)状态(即存在于当前 Hibernate Session 中),否则判定为 transient(未持久化)状态,抛出异常。
如果你的测试类没有标注 @Transactional,@BeforeEach 中的 addressRepository.save(address) 会在独立事务中执行并提交,此时 address 虽已持久化,但进入测试方法后,当前 Session 中没有该实例,它处于 detached 状态,这就是 Hibernate 报错的直接原因。
解决方案
方案一:为测试类添加事务注解
将 @Transactional 添加到测试类上,让 @BeforeEach 和测试方法共享同一个事务,这样 save 后的 address 会一直处于当前 Session 的托管状态,保存 Building 时 Hibernate 能正确识别其持久化状态:
@SpringBootTest @Transactional class YourIntegrationTest { @Autowired private AddressRepository addressRepository; @Autowired private BuildingRepository buildingRepository; @BeforeEach void init(){ Address address = new Address(); // 初始化address字段 address = addressRepository.save(address); Building building = new Building(); building.setAddress(address); buildingRepository.save(building); } // 测试方法 }
注:默认情况下,测试事务会在方法结束后自动回滚,不会污染测试数据库。
方案二:重新加载address到当前会话
如果不想使用全局事务,可以在 save address 后,通过 repository 的查询方法重新获取实例,确保它进入当前 Session 的托管状态:
@BeforeEach void init(){ Address address = new Address(); // 初始化address字段 address = addressRepository.save(address); // 重新加载,将实例纳入当前会话 address = addressRepository.findById(address.getId()).orElseThrow(); Building building = new Building(); building.setAddress(address); buildingRepository.save(building); }
方案三:关闭Hibernate严格校验(不推荐)
在测试环境配置文件(application-test.properties)中关闭空值校验,绕过这个错误,但会降低数据一致性校验能力,隐藏潜在问题:
spring.jpa.properties.hibernate.check_nullability=false
额外检查点
- 确保 Address 实体的 ID 字段配置了正确的生成策略,比如:
@Entity class Address { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 其他字段与方法 }
- 确保 Building 实体的 address 字段有合法的 getter/setter 方法,或 Hibernate 能通过反射访问该字段(比如字段为 public,或配置了
@AccessType注解)。
内容的提问来源于stack exchange,提问作者KuSpa

