【问题标题】:MultipleBagFetchException: cannot simultaneously fetch multiple bagsMultipleBagFetchException:不能同时获取多个包
【发布时间】:2021-01-15 06:17:55
【问题描述】:

我有以下实体

注册程序

@Data
@NoArgsConstructor
@Entity
@EntityListeners(RegisteredProgramAuditListener.class)
public class RegisteredProgram extends Auditable<String> {

    @OneToMany(mappedBy = "registeredProgram", cascade = CascadeType.ALL)
    @JsonBackReference
    private List<Trainer> trainerList;

    @OneToMany(mappedBy = "registeredProgram", cascade = CascadeType.ALL)
    @JsonBackReference
    private List<Official> officialList;
}

培训师

@Data
@NoArgsConstructor
@EntityListeners(TrainerAuditListener.class)
@Entity
public class Trainer extends Auditable<String> {

    @ManyToOne
    @JoinColumn(name = "REGISTERED_PROGRAM_ID", nullable = false)
    @JsonManagedReference
    private RegisteredProgram registeredProgram;

    @Type(type = "yes_no")
    private Boolean isDeleted = false;
}

官方

@Data
@NoArgsConstructor
@EntityListeners(OfficialAuditListener.class)
@Entity
public class Official extends Auditable<String> {

    @ManyToOne
    @JoinColumn(name = "REGISTERED_PROGRAM_ID", nullable = false)
    @JsonManagedReference
    private RegisteredProgram registeredProgram;

    @Type(type = "yes_no")
    private Boolean isDeleted = false;
}

基本上,我与RegisteredProgram 有多对一关系的实体(培训师注册计划,官方注册计划)。现在我有一项服务已经达到了我的要求,通过 id 获取注册程序,我应该只包含所有 TrainerOfficialisDeleted false。请参阅以下服务:

服务

@Override
public RegisteredProgramRequestDto getRegisteredProgramDto(Long id) {
    RegisteredProgram registeredProgram = registeredProgramRepository.getOne(id);
    RegisteredProgramRequestDto registeredProgramRequestDto = programRegistrationMapper
            .registeredProgramToRequestDto(registeredProgram);
    registeredProgramRequestDto.setOfficialDtoList(
            registeredProgramRequestDto.getOfficialDtoList()
                    .stream()
                    .filter(officialDto -> !officialDto.getIsDeleted())
                    .collect(Collectors.toList())
    );
    registeredProgramRequestDto.setTrainerDtoList(
            registeredProgramRequestDto.getTrainerDtoList()
                    .stream()
                    .filter(trainerDto -> !trainerDto.getIsDeleted())
                    .collect(Collectors.toList())
    );
    return registeredProgramRequestDto;
}

现在,我尝试使用@Query@EntityGraph,这样我就可以只使用单个查询获得所需的输出。

存储库

@Repository
public interface RegisteredProgramRepository extends JpaRepository<RegisteredProgram, Long>, QuerydslPredicateExecutor<RegisteredProgram> {

    @Query("select rp from RegisteredProgram rp join rp.officialList rpos join rp.trainerList rpts where rp.id = :id and rpos.isDeleted = false and rpts.isDeleted = false")
    @EntityGraph(attributePaths = {"officialList", "trainerList"}, type = EntityGraph.EntityGraphType.LOAD)
    RegisteredProgram getByIdNotDeleted(@Param("id") Long id);
}

更新服务

@Override
public RegisteredProgramRequestDto getRegisteredProgramDto(Long id) {
    RegisteredProgram registeredProgram = registeredProgramRepository.getByIdNotDeleted(id);
    return programRegistrationMapper
            .registeredProgramToRequestDto(registeredProgram);
}

但是在实现之后,我遇到了以下错误:

org.hibernate.loader.MultipleBagFetchException:无法同时获取多个包:[com.tesda8.region8.program.registration.model.entities.RegisteredProgram.officialList, com.tesda8.region8.program.registration.model.entities。 RegisteredProgram.trainerList]

我已经通过 stackoverflow 搜索并遇到了this,但我仍然无法让我的查询正确执行。关于我应该如何处理这个问题的任何想法?

【问题讨论】:

    标签: java spring hibernate jpa


    【解决方案1】:

    解决MultipleBagFetchException 的常规修复方法是在Set 类型上更改List 类型字段,如下所示:

    ...
    public class RegisteredProgram extends Auditable<String> {
    
        @OneToMany(mappedBy = "registeredProgram", cascade = CascadeType.ALL)
        @JsonBackReference
        private Set<Trainer> trainerList = new HashSet<>();
    
        @OneToMany(mappedBy = "registeredProgram", cascade = CascadeType.ALL)
        @JsonBackReference
        private Set<Official> officialList = new HashSet<>();
        ...
    }
    

    更多详情见:https://thorben-janssen.com/hibernate-tips-how-to-avoid-hibernates-multiplebagfetchexception/

    注意:记住equalshashcode 用于Set 数据结构并避免Lombok 和Hibernate 陷阱(https://thorben-janssen.com/lombok-hibernate-how-to-avoid-common-pitfalls/)。请注意“避免@Data”主题,因为我看到您正在使用该组合,该组合会产生意外行为!

    【讨论】:

    • 在从List 更改为Set 并从实体中删除@Data 并用@Getter@Setter 替换后解决了所有问题。谢谢你!
    • 这不能解决笛卡尔积。见这里:allaroundjava.com/hibernate-cartesian-product-problem 你可以做的是添加这个来独立获取选择@org.hibernate.annotations.Fetch(FetchMode.SELECT)
    猜你喜欢
    • 2011-05-19
    • 1970-01-01
    • 2016-09-13
    • 2014-08-31
    • 2015-11-28
    • 2013-05-03
    • 2017-08-20
    • 2016-09-27
    • 2012-03-16
    相关资源
    最近更新 更多