JPA标准API 动态sql和分页实现

最近,在公司的项目开发中使用到了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中引入的

标准API查询子句的方法

子句 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 from  的原因

你可能感兴趣的:(JPA,Hibernate)