带有规范的Spring分页符可以通过已加入的列

时间:2017-08-23 14:57:11

标签: spring jpa spring-data-jpa specifications

有人可以向我解释为什么Spring使用规范功能检查可分页LEFT JOIN Order吗?

情况如下:

我想使用Spring规范生成用于过滤数据的动态查询,并且可分页以限制数据。查询位于UserEntityUserGroupEntity之间,我希望始终在每个查询上获取UserGroupEntity,因此我为COUNT和数据查询添加了一个检查块。如果查询是查询COUNT,那么我使用Join对象,否则我使用Fetch对象。

public static Specification<UserEntity> filtered(final String searchTerm) {

    return new Specification<UserEntity>() {

        @SuppressWarnings("unchecked")
        @Override
        public Predicate toPredicate(Root<UserEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

            Join<UserEntity, UserGroupEntity> joinUserGroup = null;

            if (QueryHelper.isQueryCount(query)) {

                joinUserGroup = root.join("userGroup");

            } else {

                Fetch<UserEntity, UserGroupEntity> fetchUserGroup = root.fetch("userGroup");
                joinUserGroup = (Join<UserEntity, UserGroupEntity>) fetchUserGroup;
            }


            if (searchTerm != null) {

                List<Predicate> predicates = new ArrayList<Predicate>();

    predicates.add(cb.like(root.get("lowerUsername").as(String.class), "%" + searchTerm.toLowerCase() + "%"));

predicates.add(cb.like(root.get("lowerEmail").as(String.class), "%" + searchTerm.toLowerCase() + "%"));

predicates.add(cb.like(joinUserGroup.get("lowerName").as(String.class), "%" + searchTerm.toLowerCase() + "%"));

                cb.or(predicates.toArray(new Predicate[predicates.size()]));
            }

            return null;
        }
    };
}

这就是我在UserService上调用规范的方式:

Order order = new Order(Direction.ASC, "lowerEmail");
Page<UserEntity> pages = userRepository.findAll(filtered(searchTerm), new PageRequest(page, size, new Sort(order)));

使用UserEntity列添加排序时,查询正常。这是生成的HQL:

select generatedAlias0 from com.example.entity.UserEntity as generatedAlias0 
inner join fetch generatedAlias0.userGroup as generatedAlias1 
order by generatedAlias0.lowerEmail asc

但当我使用连接实体中的列进行排序userGroup.lowerName时,查询变得奇怪(因为我不知道为什么)。通过使用该列,Spring在我的查询中添加了更多LEFT JOIN并使其像这样:

select generatedAlias0 from com.example.entity.UserEntity as generatedAlias0 
left join generatedAlias0.userGroup as generatedAlias1 
inner join fetch generatedAlias0.userGroup as generatedAlias2 
order by generatedAlias1.lowerName asc

当我在Github上看到spring-data-JPA代码时,我发现从specificationcriteria的转换是在方法getQuery()中完成的。此方法调用类toOrders()中的其他方法QueryUtils以应用排序顺序。方法toOrders()最终调用方法getOrCreateJoin()isAlreadyFetched(),它将检查前一个连接属性(我在规范中创建的属性)userGroup,但是因为连接类型不是LEFT JOIN然后Spring在我的查询中使用LEFT JOIN添加了更多连接。

private static Join<?, ?> getOrCreateJoin(From<?, ?> from, String attribute) {

    for (Join<?, ?> join : from.getJoins()) {

        boolean sameName = join.getAttribute().getName().equals(attribute);

        if (sameName && join.getJoinType().equals(JoinType.LEFT)) {
            return join;
        }
    }

    return from.join(attribute, JoinType.LEFT);
}

这是我在spring-data-jpa代码CMIIW上搜索得到的内容。 JoinType.LEFT的目的是什么,实际上我还是不明白。你的解释对我(和我们)非常有帮助。

现在我认为我将使用自定义存储库使用JPQL生成动态查询,直到我使用LEFT JOINspecification了解生成的查询与其他pageable的原因。

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,并在https://stackoverflow.com/a/43965633/7280115中找到了解决方案和答案。您可以检查它是否也适合您