JPA Criteria API计数

7
我一直在尝试获取总行数,为此我使用了JPA Criteria API,但是在Long count = em.createQuery(sc).getSingleResult();这一行中出现错误,报告java.lang.IllegalStateException: No criteria query roots were specified。我做了一些研究,但无法缩小问题范围。
以下是我的代码片段:
@PersistenceContext
    public EntityManager em;

......

public Page<UserDTO> findByCriteria(String filters, Pageable pageable) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<UserDTO> cq = cb.createQuery(UserDTO.class);
        Root<UserDTO> iRoot = cq.from(UserDTO.class);
        List<Predicate> predicates = new ArrayList<Predicate>();

        if (StringUtils.isNotEmpty(filters)) {
            predicates.add(cb.like(cb.lower(iRoot.<String>get("login")), "%" + filters.toLowerCase() + "%"));
        }

        Predicate[] predArray = new Predicate[predicates.size()];
        predicates.toArray(predArray);

        CriteriaQuery<Long> sc = cb.createQuery(Long.class);
        sc.select(cb.count(iRoot));

        sc.where(predArray);
        Long count = em.createQuery(sc).getSingleResult();

        cq.where(predArray);

        List<Order> orders = new ArrayList<Order>(2);
        orders.add(cb.asc(iRoot.get("name")));
        orders.add(cb.asc(iRoot.get("desc")));

        cq.orderBy(orders);
        TypedQuery<UserDTO> query = em.createQuery(cq);

        Page<UserDTO> result = new PageImpl<UserDTO>(query.getResultList(), pageable, count);


        return result;
    }

编辑后且可工作的代码:

public Page<UserDTO> findByCriteria(String columnName, String filters, Pageable pageable) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<UserDTO> cq = cb.createQuery(UserDTO.class);
        Root<UserDTO> iRoot = cq.from(UserDTO.class);
        List<Predicate> predicates = new ArrayList<Predicate>();

        if (StringUtils.isNotEmpty(filters)) {
            predicates.add(cb.like(cb.lower(iRoot.<String>get(columnName)), "%" + filters.toLowerCase() + "%"));
        }

        Predicate[] predArray = new Predicate[predicates.size()];
        predicates.toArray(predArray);

        Long count = calculateCount(filters);

        cq.where(predArray);

        List<Order> orders = new ArrayList<Order>(2);
        orders.add(cb.asc(iRoot.get("firstName")));
        orders.add(cb.asc(iRoot.get("lastName")));

        cq.orderBy(orders);
        TypedQuery<UserDTO> query = em.createQuery(cq);

        Page<UserDTO> result = new PageImpl<UserDTO>(query.getResultList(), pageable, count);


        return result;
    }

    public Long calculateCount(String filters) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Long> sc = cb.createQuery(Long.class);
        Root<UserDTO> iRoot = sc.from(UserDTO.class);
        List<Predicate> predicates = new ArrayList<Predicate>();

        if (StringUtils.isNotEmpty(filters)) {
            predicates.add(cb.like(cb.lower(iRoot.<String>get("login")), "%" + filters.toLowerCase() + "%"));
        }

        Predicate[] predArray = new Predicate[predicates.size()];
        predicates.toArray(predArray);

        sc.select(cb.count(iRoot));

        sc.where(predArray);
        Long count = em.createQuery(sc).getSingleResult();

        return count;

    }

@XtremeBaumer 抱歉,我以为他缺少了 cq.select(qb.count(cq.from(MyEntity.class))); - Scary Wombat
我尝试了那个解决方案,但它似乎并不起作用。这就是为什么我发布了这个问题。也许是我做错了什么。@XtremeBaumer - Burak N.
1
你有没有尝试创建一个只执行count操作的方法? - Scary Wombat
1
我认为最好的方法是将每个查询外包,并从中创建结果。 - XtremeBaumer
2
我猜想如果你在旧代码中添加这行 sc.from(UserDTO.class);,那么它也会起作用。这行代码在旧代码中缺失,并指定查询应从哪个“Root”类选择。 - XtremeBaumer
显示剩余10条评论
1个回答

3

如评论所预期的那样,您需要明确添加CriteriaQuery#from()方法:

sc.from(UserDTO.class);

from方法通常不使用,因为API提供程序默认使用CriteriaQuery构造函数中指定的实体类。但是在这种情况下不会这样做,因为该类是Long,并且它不对应于实体类。

另请参阅:

Oracle的JAVA EE教程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接