最近,在公司的项目开发中使用到了JPA 标准API ,如
javax.persistence.criteria.CriteriaBuilder
javax.persistence.criteria.CriteriaQuery
这两个接口。如果是固定条件的查询,可以使用对应的配置文件,如
SELECT _t from table_name
或注解,如
@NamedQuery(name="queryName" , query="SELECT * FROM table_name ")
的方式,通过
EntityManager.createNamedQuery("queryName")
来完成查询操作 ,但是在实际的开发中,往往会用到动态的条件查询,那么上面的那种方式就不能解决我们的问题了,这个时候前面提到的两个接口就能很好的解决这个问题。
标准(Criteria)API是构建实体及其持久状态查询的最常用方法之一。 它只是定义JPA查询的另一种方法。
Criteria API定义了一个独立于平台的条件查询,用Java编程语言编写。 它是在JPA 2.0中引入的
子句 | Criteria API接口 | 方法 |
---|---|---|
SELECT | CriteriaQuery | select() |
FROM | AbstractQuery | from() |
WHERE | AbstractQuery | where() |
ORDER BY | CriteriaQuery | orderBy() |
GROUP BY | AbstractQuery | groupBy() |
HAVING | AbstractQuery | having() |
CriteriaQuery
接口是AbstractQuery
接口的子接口。
以上是JPA条件查询中常用的方法。
要创建标准查询,请按照以下步骤操作: -
第1步: 通过在EntityManager
接口实例上调用getCriteriaBuilder()
方法创建CriteriaBuilder
接口的对象。
EntityManager em = emf.createEntityManager();
CriteriaBuilder cb=em.getCriteriaBuilder();
第2步: 构建一个CriteriaQuery
接口的实例来创建一个查询对象。
CriteriaQuery cq=cb.createQuery(StudentEntity.class);
第3步: 从CriteriaQuery
对象的方法调用来设置查询根。
Root stud=cq.from(StudentEntity.class);
第4步: 调用CriteriaQuery
对象的select()
方法来指定查询结果的类型。
CriteriaQuery select = cq.select(stud);
第5步: 创建Query
接口的实例并指定用于访问数据库记录的方法的类型。
Query q = em.createQuery(select);
第6步: 通过调用查询接口的方法来控制查询的执行。
List list = q.getResultList();
以上步骤的代码来自 这 里 。
根据上面的步骤,自己做了一些改动,请参考以下代码:
//实体类
@Entity
@Table(name = "student")
public class Student {
@Id
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//服务类
public class StudentServiceBean implements StudentService {
@PersistenceContext
EntityManager em;
public PagedList list(String name, int page){
CustomPredicate predicate = new CustomPredicate() {
@Override
public List whereWarpper(CriteriaBuilder criteriaBuilder, Root root) {
List where = new ArrayList<>();
if(StringUtils.isNotBlank(title)) {
where.add(criteriaBuilder.equal(root.get("name"),name));
}
return where;
}
};
return this.criteriaQuery(Student.class, predicate, page, 10);
}
//将分页方法抽象出来
private PagedList criteriaQuery( Class obj , CustomPredicate predicate , int page , int size ){
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(obj);
Root From = criteriaQuery.from(obj);
List where = predicate.whereWarpper(criteriaBuilder , From);
Predicate[] params ;
if(where != null && where.size() > 0) {
params = where.toArray(new Predicate[where.size()]);
}else {
params = new Predicate[0];
}
criteriaQuery.where(params);
int start = (page -1 )*size;
List selectResult = em.createQuery(criteriaQuery).setFirstResult(start).setMaxResults(size).getResultList();
CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.where(params);
countQuery.select(criteriaBuilder.count(countQuery.from(obj)));
int total = em.createQuery(countQuery).getSingleResult().intValue();
return new PagedList<>(selectResult, page, total);
}
//动态条件组合接口,将条件通过此接口整合到List中
interface CustomPredicate{
List whereWarpper(CriteriaBuilder criteriaBuilder , Root root);
}
}
这样,JPA标准API 动态条件查询和分页就能实现了。
这里有一个地方需要注意一下,如果代码的第82行优先于第77行执行的话,就会报一个错误
17:56:25,700 ERROR [org.hibernate.hql.internal.ast.ErrorCounter] (default task-1) Invalid path: 'generatedAlias1.field' //field为字段名
17:56:25,701 ERROR [org.hibernate.hql.internal.ast.ErrorCounter] (default task-1) Invalid path: 'generatedAlias1.field': Invalid path: 'generatedAlias1.field' //field为字段名
观察代码可以发现是Root