学习JPA笔记——构建复杂查询
2020-10-06 本文已影响0人
NatsuSai

封面:同上篇,这次前景就是最高机密 Viper Zero。
这里介绍两种 JPA 做复杂查询的方法,一个是用 SpringDataJPA 实现, 一个是用 Java EE 实现。
SpringDataJPA
SpringDataJPA 的复杂查询除了直接写 sql,按照规则定义 Repository 接口方法以外,还可以使用Specification做查询。
Specification
这是SpringDataJPA抽象出的一个接口,故并不一定通用于其他JPA的实现。
该接口重点在于toPredicate
方法,该方法将创建一个 where 语句对象。
public interface Specification<T> extends Serializable {
...
/**
* Creates a WHERE clause for a query of the referenced entity in form of a Predicate for the given
* Root and CriteriaQuery.
*/
@Nullable
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}
这里我只做简要说明,以便于理解,仅供参考。
- root:
一般指实体类本身,包装成 Root 对象 - query:
sql语句对象,一般在此方法内部不做调用,下面会给一些用于理解的调用。 - criteriaBuilder:
用于构建条件语句。
Getting Started
首先我们创建一个 Repository 接口,并继承JpaSpecificationExecutor<T>
以获得复杂查询的能力。
public interface PostRepository extends JpaSpecificationExecutor<Post> {
}
然后我们运用 java8 所带来的新特性,使用 lambda 构建一个匿名 Specification 的实现类,并实现 toPredicate 方法。
repository.findAll(
(Specification<Post>) (root, criteriaQuery, builder) -> {
// where title like '%Test%';
return builder.like(root.get(Post_.title), "%Test%")
// 如果是多个条件,例如 where title like '%Test%' and content like '%Test%' and id < 10;
return builder.and(builder.like(root.get(Post_.title), "%Test%"), builder.like(root.get(Post_.content), "%Test%"), builder.le(root.get(Post_.id), 10))
// 而如果我们没有做 Typesafe,那么就会变成这样
return builder.like(root.get("title"), "%Test%")
}
);
使用起来其实没什么困难,基本举一反三,其他的复杂查询我暂时没研究,主要是觉得可以避开用别的方法操作,或者提到程序中操作。
Java EE
实际上,SpringDataJPA 是对 Java EE 原本的 JPA 抽象再次包装了一层,所以这个可以说是原汁原味了。
- EntityManager
实体管理类,用于与持久化上下文进行互动,核心类。
其他几个 Root、CriteriaQuery、CriteriaBuilder 作用同上,毕竟spring只是做了封装。
Getting Stated
由于我主要使用 SpringDataJPA 所以摘抄了一段代码,出处。
//需要对此进行注入
private final EntityManage entityManager;
String q;
int offset, limit;
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
// create query
CriteriaQuery<Post> query = cb.createQuery(Post.class);
// set the root class
Root<Post> root = query.from(Post.class);
// if keyword is provided
if (q != null && !q.trim().isEmpty()) {
// 这里其实就是上面 toPredicate 返回的对象作为参数传入where方法当中,所以里面就和上面的实现没有什么太大区别。
query.where(
cb.or(
cb.like(root.get(Post_.title), "%" + q + "%"),
cb.like(root.get(Post_.content), "%" + q + "%")
)
);
}
//perform query
return this.entityManager.createQuery(query)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
看完上面代码,大致就能够了解清楚这几个类分别是怎么使用的了,总体来说其实比上面spring的实现所接触到的东西更加全面一些。