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

最近,在公司的项目开发中使用到了JPA 标准API ,如

javax.persistence.criteria.CriteriaBuilder 
javax.persistence.criteria.CriteriaQuery<T>

这两个接口。如果是固定条件的查询,可以使用对应的配置文件,如

<entity-mappings>
    <named-query name="queryName">
        <query>
            SELECT _t from table_name 
        </query>
    </named-query>
</entity-mappings>

或注解,如

@NamedQuery(name="queryName" , query="SELECT * FROM table_name ")

的方式,通过

EntityManager.createNamedQuery("queryName")

来完成查询操作 ,但是在实际的开发中,往往会用到动态的条件查询,那么上面的那种方式就不能解决我们的问题了,这个时候前面提到的两个接口就能很好的解决这个问题。

标准(Criteria)API是构建实体及其持久状态查询的最常用方法之一。 它只是定义JPA查询的另一种方法。

Criteria API定义了一个独立于平台的条件查询,用Java编程语言编写。 它是在JPA 2.0中引入的

标准API查询子句的方法

子句Criteria API接口方法
SELECTCriteriaQueryselect()
FROMAbstractQueryfrom()
WHEREAbstractQuerywhere()
ORDER BYCriteriaQueryorderBy()
GROUP BYAbstractQuerygroupBy()
HAVINGAbstractQueryhaving()

CriteriaQuery接口是AbstractQuery接口的子接口。

以上是JPA条件查询中常用的方法。

创建条件查询的步骤

要创建标准查询,请按照以下步骤操作: -

第1步: 通过在EntityManager接口实例上调用getCriteriaBuilder()方法创建CriteriaBuilder接口的对象。

EntityManager em = emf.createEntityManager();  
CriteriaBuilder cb=em.getCriteriaBuilder();

第2步: 构建一个CriteriaQuery接口的实例来创建一个查询对象。

CriteriaQuery<StudentEntity> cq=cb.createQuery(StudentEntity.class);

第3步: 从CriteriaQuery对象的方法调用来设置查询根。

Root<StudentEntity> stud=cq.from(StudentEntity.class);

第4步: 调用CriteriaQuery对象的select()方法来指定查询结果的类型。

CriteriaQuery<StudentEntity> select = cq.select(stud);

第5步: 创建Query接口的实例并指定用于访问数据库记录的方法的类型。

Query q = em.createQuery(select);

第6步: 通过调用查询接口的方法来控制查询的执行。

List<StudentEntity> 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<Student> list(String name,  int page){
		
		CustomPredicate<Student> predicate = new CustomPredicate<Student>() {
			@Override
			public List<Predicate> whereWarpper(CriteriaBuilder criteriaBuilder, Root<Student> root) {
				
				List<Predicate> 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 <T> PagedList<T> criteriaQuery( Class<T> obj , CustomPredicate<T> predicate , int page , int size ){
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		
		CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(obj);
		
		Root<T> From = criteriaQuery.from(obj);
		
		List<Predicate> 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<T> selectResult = em.createQuery(criteriaQuery).setFirstResult(start).setMaxResults(size).getResultList();
		
		CriteriaQuery<Long> 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<T>{
		 List<Predicate> whereWarpper(CriteriaBuilder criteriaBuilder , Root<T> 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<T> from  的原因

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值