MultipleBagFetchException与HttpMessageNotWritableException异常求解
问题根因说明
MultipleBagFetchException的触发原因是Hibernate不支持同时对两个及以上的集合类型关联属性执行急加载(EAGER)。你之前的全局EAGER配置会触发所有涉及Expense的查询都自动加载关联属性,如果关联的Shop或Currency实体中还存在其他EAGER加载的List类型关联,就会触发该异常。
而你切换为懒加载后接口抛出HttpMessageNotWritableException,是因为Spring MVC序列化Expense对象时,关联的shop、currency还只是未初始化的代理对象,session已经关闭无法加载数据导致的。
可行解决方案(无需修改集合类型)
方案1:使用实体图按需指定加载关联属性(推荐)
- 先将Expense实体的关联属性改回懒加载:
@ManyToOne(fetch = FetchType.LAZY) private Shop shop; @ManyToOne(fetch = FetchType.LAZY) private Currency currency;
- 在你的
ExpenseRepository的findAll方法上添加@EntityGraph注解,指定查询时同步加载shop和currency:
import org.springframework.data.jpa.repository.EntityGraph; import java.util.List; public interface ExpenseRepository extends JpaRepository<Expense, Long> { @EntityGraph(attributePaths = {"shop", "currency"}) List<Expense> findAll(); // 其他查询方法如果需要关联数据也可以加同样的注解 @EntityGraph(attributePaths = {"shop", "currency"}) List<Expense> findAllByShopId(Long shopId); }
这种方式只有标注了实体图的查询会加载关联属性,其他查询默认走懒加载,既不会触发MultipleBagFetchException,也能保证findAll接口返回的对象关联属性已经初始化,可以正常序列化。
方案2:使用JPQL的JOIN FETCH手动指定关联加载
如果你是用自定义JPQL查询,可以直接在查询语句中写JOIN FETCH指定要加载的关联:
import org.springframework.data.jpa.repository.Query; import java.util.List; public interface ExpenseRepository extends JpaRepository<Expense, Long> { @Query("SELECT e FROM Expense e JOIN FETCH e.shop JOIN FETCH e.currency") List<Expense> findAll(); }
效果和实体图完全一致,适合需要自定义查询逻辑的场景。
临时规避方案
如果暂时不想修改查询逻辑,可以在application.properties/yml中添加配置,关闭序列化时的空Bean报错:
spring.jackson.serialization.fail-on-empty-beans=false
但该方案只是跳过了序列化报错,实际返回的关联字段值为null,仅适合临时调试使用,不建议线上使用。
内容的提问来源于stack exchange,提问作者Erick Caron
相关产品推荐
相关产品推荐

