save()后直接调用findAll(),OneToMany集合返回null问题咨询
嘿,这个问题我之前也碰到过,结合你的代码和描述,我来帮你拆解下原因和解决办法~
问题根源分析
1. 双向关联未正确维护
你的实体类是双向的OneToMany/ManyToOne关联,但在插入测试数据时,只设置了ManyToOne的一端(比如wag.setMainGroup(wug)),却没有同步更新OneToMany的一端(比如wug.getSubGroups().add(wag))。JPA不会自动帮你维护双向关联的两边,这就导致持久化上下文里的实体关联不完整——哪怕数据库里的外键是正确的,后续查询时Hibernate也无法正确加载集合。
2. 持久化上下文生命周期问题
如果testInsert和findWHG不在同一个事务里,每次save都会开启并关闭独立事务,插入后的实体会变成游离态。当你调用findAll时,新插入实体的subGroups是懒加载的,此时没有活跃的EntityManager,Hibernate无法初始化这个集合,就会返回null;而重复插入时,旧的实体已经在数据库中,第二次查询时Hibernate可能从二级缓存或重新加载完整实体,所以集合能正常返回。
解决方案
1. 手动维护双向关联关系
插入数据时,一定要同步设置关联的两边,让JPA识别完整的关联关系:
public void testInsert() { WHG whg = new WHG(); whg.setName("Med1"); whg.setIcon(Icon.MEDKIT); whg.setWareGroupMain(true); WHG wug = new WHG(); wug.setName("Med2"); wug.setIcon(Icon.DRINK2); wug.setWareGroupMain(true); WHG wag = new WHG(); wag.setName("Med3"); wag.setIcon(Icon.CUBES); wag.setWareGroupMain(false); WHG wig = new WHG(); wig.setName("Med4"); wig.setIcon(Icon.BOLT); wig.setWareGroupMain(false); // 同步维护双向关联 wag.setMainGroup(wug); wug.getSubGroups().add(wag); // 新增这行,关联wug和wag wig.setMainGroup(wag); wag.getSubGroups().add(wig); // 新增这行,关联wag和wig whgService.save(wig); whgService.save(whg); whgService.save(wug); whgService.save(wag); // ... }
2. 让查询处于事务上下文内
给findWHG方法加上@Transactional注解,确保查询时EntityManager处于活跃状态,懒加载的集合能正常初始化:
@Service public class WHGService { @Autowired WHGRepository whgRepository; @Transactional // 新增事务注解 public List<WHG> findWHG() { List<WHG> allWHG = whgRepository.findAll(); List<WHG> result = new ArrayList<WHG>(); Stream<WHG> whgStream = allWHG.stream(); whgStream.forEach((whg) -> { if (whg.isWareGroupMain()) { result.add(whg); } }); whgStream.close(); return result; } // ... }
也可以直接在testInit方法上添加@Transactional,让插入和查询在同一个事务中执行,实体始终处于持久化上下文内。
3. 使用JOIN FETCH主动加载集合
如果不想依赖懒加载,可以在Repository中自定义查询,用JOIN FETCH主动加载subGroups:
@Repository public interface WHGRepository extends JpaRepository<WHG,Long>{ @Query("SELECT w FROM WHG w LEFT JOIN FETCH w.subGroups") List<WHG> findAllWithSubGroups(); }
然后在findWHG方法中调用findAllWithSubGroups()代替findAll(),这样查询时会一次性加载所有关联的集合,即使在事务外也能正常访问。
另外,你提到的「Hibernate应返回空集合而非null」——其实只要正确初始化集合(你已经做了new HashSet<WHG>()),在持久化上下文内访问时应该是一个空集合而非null;但如果是游离态且懒加载未初始化,就可能出现null的情况,上面的方案应该能解决这个问题。
内容的提问来源于stack exchange,提问作者foAW

