是否可以通过JPA和CriteriaBuilder的组合键进行排序

时间:2012-04-15 10:46:57

标签: java jpa criteria-api

我想使用JPA CriteriaBuilder创建一个查询,我想添加一个ORDER BY子句。这是我的实体:

@Entity
@Table(name = "brands")
public class Brand implements Serializable {

    public enum OwnModeType {
        OWNER, LICENCED
    }

    @EmbeddedId
    private IdBrand id;
    private String code;
    //bunch of other properties
}

嵌入式课程是:

@Embeddable
public class IdBrand implements Serializable {

    @ManyToOne
    private Edition edition;
    private String name;
}

我构建查询的方式是这样的:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Brand> q = cb.createQuery(Brand.class).distinct(true);
Root<Brand> root = q.from(Brand.class);
if (f != null) {
    f.addCriteria(cb, q, root);
    f.addOrder(cb, q, root, sortCol, ascending);
}
return em.createQuery(q).getResultList();

以下是名为的函数:

public void addCriteria(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r) {
}

public void addOrder(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r, String sortCol, boolean ascending) {
    if (ascending) {
        q.orderBy(cb.asc(r.get(sortCol)));
    } else {
        q.orderBy(cb.desc(r.get(sortCol)));
    }
}

如果我尝试将sortCol设置为"id.name",我会收到以下错误:

  

javax.ejb.EJBException:java.lang.IllegalArgumentException:无法解析路径

的属性[id.name]

知道我怎么能做到这一点?我尝试在网上搜索,但是我找不到关于这一点的提示...如果我在ORDER BY关系时可以做类似@ManyToOne的话也会很棒(例如{{1} }})

2 个答案:

答案 0 :(得分:5)

问题是您对JPA路径使用情况的理解。 JPA Manual说:

  

可以扩展其类型是可持久用户类的路径表达式   进一步重复使用点(。)运算符。例如,c.capital.name   是一个从Capital实体继续的嵌套路径表达式   对象到其名称字段。 可以进一步扩展路径表达式   仅当其类型也是用户定义的可持久类时。点(。)   operator不能应用于简单的集合,映射和值   类型(数字,布尔值,字符串,日期)。

在您的情况下,您使用了id.edition.number,其中 IdBrand(id)不是用户可持久的类。

解决方案以这种方式构建路径表达式:root.get("id").get("edition.number");


为了避免此类问题,您可以编写一个路径构建器,它基于javax.persistence.metamodel.Attribute.PersistentAttributeType来决定路径的构建方式。对于基本元素 - 使用.get(),对于集合元素使用.join()。

要了解您的错误,您需要阅读有关JPA路径的更多信息。 Here is a good manual on JPA paths

答案 1 :(得分:1)

我实际上在另一个类似的问题上找到了答案:JPA - Criteria API and EmbeddedId

以下是我修复代码的方法:

public void addOrder(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r, String sortCol, boolean ascending) {
    Path<?> p = r;
    String []sortCols = sortCol.split("\\."); // This is a regexp, hence the escape backslash
    for(String sc: sortCols) {
        p = p.get(sc);
    }
    if (ascending) {
        q.orderBy(cb.asc(p));
    } else {
        q.orderBy(cb.desc(p));
    }
}