升级至Spring Boot 3.4.X后出现TransientObjectException问题求助
问题描述
我的Spring Boot应用包含Catalog与CatalogCategory(关联表)两个实体,在Spring Boot 3.3.7版本中,以下代码可正常执行:
public void deepDelete(Category category) { //Catalog-category catalogCategoryService.deleteByCategory(category); delete(category); }
但升级至Spring Boot 3.4.0后,出现如下TransientObjectException异常:
12T14:17:15.098+02:00 ERROR 26596 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of 'com.techi.gox.model.Category' (save the transient instance before flushing)] with root cause org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of 'com.techi.gox.model.Category' (save the transient instance before flushing) at org.hibernate.engine.spi.CascadingActions$9.cascade(CascadingActions.java:382) ~[hibernate-core-6.6.2.Final.jar:6.6.2.Final] at org.hibernate.engine.spi.CascadingActions$9.cascade(CascadingActions.java:372) ~[hibernate-core-6.6.2.Final.jar:6.6.2.Final]
请问哪里操作有误?为何新版本会触发该异常?
问题原因
Spring Boot 3.4.0对应的Hibernate版本升级到了6.6.x,这个版本对实体关联的状态检查更严格。当前操作流程中,catalogCategoryService.deleteByCategory(category)执行后,Hibernate可能已将category实体的状态从持久化变为游离/瞬态;后续执行delete(category)时,Hibernate检测到该实体已脱离持久化上下文,而关联的CatalogCategory操作可能残留对它的引用,从而触发异常。
另外,也可能是实体关联配置(比如Catalog和CatalogCategory之间的级联关系)在新版本Hibernate下的行为变化导致——旧版本允许对游离实体执行删除,新版本则严格要求操作的实体必须处于持久化状态。
解决办法
确保删除时Category处于持久化状态
先通过ID重新加载Category实体,再执行删除操作,保证操作对象始终处于持久化上下文:public void deepDelete(Category category) { // 重新加载持久化状态的Category Category persistentCategory = categoryRepository.findById(category.getId()).orElseThrow(); // 删除关联表记录 catalogCategoryService.deleteByCategory(persistentCategory); // 删除持久化状态的实体 delete(persistentCategory); }利用级联删除简化逻辑
如果CatalogCategory和Category是多对一关联,可在Category的关联集合上配置级联删除,这样删除Category时会自动清理关联的CatalogCategory记录,无需手动调用删除方法:// 在Category实体中配置 @OneToMany(mappedBy = "category", cascade = CascadeType.REMOVE) private List<CatalogCategory> catalogCategories;简化后的删除代码:
public void deepDelete(Category category) { delete(category); }控制事务边界
确保整个删除操作在同一个事务中执行,避免中间操作导致实体状态脱离持久化上下文。可在deepDelete方法上添加@Transactional注解:@Transactional public void deepDelete(Category category) { catalogCategoryService.deleteByCategory(category); delete(category); }
内容的提问来源于stack exchange,提问作者Luis

