首页 文章

JPA Criteria使用带有连接而不是多个查询的SINGLE查询来查询急切获取关联实体

提问于
浏览
6

我们正在从Hibernate本机标准转向JPA标准查询,将hibernate从4.3.11升级到5.2.12,并发现了不同的行为 . 以前的休眠标准使用带有连接的单个查询来急切获取一对多关联实体,但JPA使用单独的查询来获取每个根实体的关联实体 .

我知道我可以像 entityRoot.fetch("attributes", JoinType.INNER); 那样明确设置获取模式,但是我们需要在一些AbstractDao实现中执行它,该实现应该适用于任何急切的一对多关联,因此无法明确设置它 .

因此,我可以以某种方式告诉JPA标准,在默认情况下使用连接而不是针对每个根实体的单独查询,在单个查询中急切获取关联实体吗?

代码示例:

CriteriaBuilder builder = createCriteriaBuilder();
    CriteriaQuery<T> criteriaQuery = builder.createQuery(getEntityClass());
    Root<T> entityRoot = criteriaQuery.from(getEntityClass());

    criteriaQuery.select(entityRoot);
    criteriaQuery.where(builder.equal(entityRoot.get("param1"), "value"));

    return getEntityManager().createQuery(criteriaQuery).getResultList();

1 回答

  • 0

    简答

    您无法以这种方式配置它,但您可以实现必要的行为 .

    答案很长

    正如您在Hibernate 5.2 User Guide中所读到的,有几种方法可以应用提取策略:

    @Fetch annotation是一种应用提取策略的静态方法, FetchMode.JOIN 完全按照您的描述工作:

    本质上是EAGER的抓取方式 . 要获取的数据是通过使用SQL外连接获得的 .

    问题是,即使您使用 @Fetch(FetchMode.JOIN) 注释标记 attributes 集合,它也将是overridden

    我们之所以不使用JPQL查询来获取多个Department实体,是因为查询获取指令会覆盖FetchMode.JOIN策略 . 要使用JPQL查询获取多个关系,必须使用JOIN FETCH指令 . 因此,FetchMode.JOIN对于通过其标识符或natural-id直接获取实体时非常有用 .

    没有 FetchParent::fetch 的JPA Criteria查询也会这样做 .

    由于您需要一个抽象DAO的通用解决方案,可能的方法是使用反射处理所有渴望的一对多关联:

    Arrays.stream(getEntityClass().getDeclaredFields())
                .filter(field ->
                        field.isAnnotationPresent(OneToMany.class))
                .filter(field ->
                        FetchType.EAGER == field.getAnnotation(OneToMany.class).fetch())
                .forEach(field ->
                        entityRoot.fetch(field.getName(), JoinType.INNER));
    

    当然,为每个查询调用反射都是低效的 . 您可以从Metamodel获取所有已加载的 @Entity 类,对其进行处理,并存储结果以供进一步使用:

    Metamodel metamodel = getEntityManager().getMetamodel();
        List<Class> entityClasses = metamodel.getEntities().stream()
                .map(Type::getJavaType)
                .collect(Collectors.toList());
    
        Map<Class, List<String>> fetchingAssociations = entityClasses.stream()
                .collect(Collectors.toMap(
                        Function.identity(),
                        aClass -> Arrays.stream(aClass.getDeclaredFields())
                                .filter(field -> 
                                        field.isAnnotationPresent(OneToMany.class))
                                .filter(field -> 
                                        FetchType.EAGER == field.getAnnotation(OneToMany.class).fetch())
                                .map(Field::getName)
                                .collect(Collectors.toList())
                ));
    

相关问题