jpa对于固定参数的条件查询比较简单,可以在Repository中直接用参数名来查询。但是对于不固定的参数查询就比较麻烦了,官方提供的是继承JpaSpecificationExecutor,然后自己拼接Specification。这一篇主要是对Specification进行封装,让写法更友好.
代码参考:http://lee1177.iteye.com/blog/1994295。感觉还不够完整,回头使用中再补上。
创建条件表达式接口,模拟系统的条件查询
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
public interface Criterion {
enum Operator {
EQ, NE, LIKE, GT, LT, GTE, LTE, AND, OR, IS_MEMBER, IS_NOT_MEMBER
}
Predicate toPredicate(Root> root, CriteriaQuery> query,
CriteriaBuilder builder);
}
创建一个自定义的Sepcification,添加add方法用来添加多个条件
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 定义一个查询条件容器 on 17/6/6.
*/
public class Criteria<T> implements Specification<T> {
private List criterions = new ArrayList<>();
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query,
CriteriaBuilder builder) {
if (!criterions.isEmpty()) {
List predicates = new ArrayList<>();
for (Criterion c : criterions) {
predicates.add(c.toPredicate(root, query, builder));
}
// 将所有条件用 and 联合起来
if (predicates.size() > 0) {
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
return builder.conjunction();
}
/**
* 增加简单条件表达式
*
* @Methods Name add
* @Create In 2012-2-8 By lee
*/
public void add(Criterion criterion) {
if (criterion != null) {
criterions.add(criterion);
}
}
}
import org.springframework.util.StringUtils;
import javax.persistence.criteria.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 简单条件表达式
*
* @author lee
*/
public class SimpleExpression implements Criterion {
/**
* 属性名
*/
private String fieldName;
/**
* 对应值
*/
private Object value;
/**
* 计算符
*/
private Operator operator;
protected SimpleExpression(String fieldName, Object value, Operator operator) {
this.fieldName = fieldName;
this.value = value;
this.operator = operator;
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Predicate toPredicate(Root> root, CriteriaQuery> query,
CriteriaBuilder builder) {
Path expression;
//此处是表关联数据,注意仅限一层关联,如user.address,
//查询user的address集合中,address的name为某个值
if (fieldName.contains(".")) {
String[] names = StringUtils.split(fieldName, ".");
//获取该属性的类型,Set?List?Map?
expression = root.get(names[0]);
Class clazz = expression.getJavaType();
if (clazz.equals(Set.class)) {
SetJoin setJoin = root.joinSet(names[0]);
expression = setJoin.get(names[1]);
} else if (clazz.equals(List.class)) {
ListJoin listJoin = root.joinList(names[0]);
expression = listJoin.get(names[1]);
} else if (clazz.equals(Map.class)) {
MapJoin mapJoin = root.joinMap(names[0]);
expression = mapJoin.get(names[1]);
} else {
//是many to one时
expression = expression.get(names[1]);
}
} else {
//单表查询
expression = root.get(fieldName);
}
switch (operator) {
case EQ:
return builder.equal(expression, value);
case NE:
return builder.notEqual(expression, value);
case LIKE:
return builder.like((Expression) expression, "%" + value + "%");
case LT:
return builder.lessThan(expression, (Comparable) value);
case GT:
return builder.greaterThan(expression, (Comparable) value);
case LTE:
return builder.lessThanOrEqualTo(expression, (Comparable) value);
case GTE:
return builder.greaterThanOrEqualTo(expression, (Comparable) value);
case IS_MEMBER:
return builder.isMember(value, expression);
case IS_NOT_MEMBER:
return builder.isNotMember(value, expression);
default:
return null;
}
}
}
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
/**
* 逻辑条件表达式 用于复杂条件时使用,如单属性多对应值的OR查询等
*
* @author lee
*/
public class LogicalExpression implements Criterion {
/**
* 逻辑表达式中包含的表达式
*/
private Criterion[] criterion;
/**
* 计算符
*/
private Operator operator;
public LogicalExpression(Criterion[] criterions, Operator operator) {
this.criterion = criterions;
this.operator = operator;
}
@Override
public Predicate toPredicate(Root> root, CriteriaQuery> query,
CriteriaBuilder builder) {
List predicates = new ArrayList<>();
for (int i = 0; i < this.criterion.length; i++) {
predicates.add(this.criterion[i].toPredicate(root, query, builder));
}
switch (operator) {
case OR:
return builder.or(predicates.toArray(new Predicate[predicates.size()]));
default:
return null;
}
}
}
这两个类分别模拟不同的条件查询。
import org.springframework.util.StringUtils;
import java.util.Collection;
/**
* 条件构造器
* 用于创建条件表达式
*
* @Class Name Restrictions
* @Author lee
*/
public class Restrictions {
/**
* 等于
*/
public static SimpleExpression eq(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.EQ);
}
/**
* 集合包含某个元素
*/
public static SimpleExpression hasMember(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.IS_MEMBER);
}
/**
* 不等于
*/
public static SimpleExpression ne(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.NE);
}
/**
* 模糊匹配
*/
public static SimpleExpression like(String fieldName, String value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.LIKE);
}
/**
*/
// public static SimpleExpression like(String fieldName, String value,
// MatchMode matchMode, boolean ignoreNull) {
// if (StringUtils.isEmpty(value)) return null;
// return null;
// }
/**
* 大于
*/
public static SimpleExpression gt(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.GT);
}
/**
* 小于
*/
public static SimpleExpression lt(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.LT);
}
/**
* 小于等于
*/
public static SimpleExpression lte(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.GTE);
}
/**
* 大于等于
*/
public static SimpleExpression gte(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) {
return null;
}
return new SimpleExpression(fieldName, value, Criterion.Operator.LTE);
}
/**
* 并且
*/
public static LogicalExpression and(Criterion... criterions) {
return new LogicalExpression(criterions, Criterion.Operator.AND);
}
/**
* 或者
*/
public static LogicalExpression or(Criterion... criterions) {
return new LogicalExpression(criterions, Criterion.Operator.OR);
}
/**
* 包含于
*/
@SuppressWarnings("rawtypes")
public static LogicalExpression in(String fieldName, Collection value, boolean ignoreNull) {
if (ignoreNull && (value == null || value.isEmpty())) {
return null;
}
SimpleExpression[] ses = new SimpleExpression[value.size()];
int i = 0;
for (Object obj : value) {
ses[i] = new SimpleExpression(fieldName, obj, Criterion.Operator.EQ);
i++;
}
return new LogicalExpression(ses, Criterion.Operator.OR);
}
/**
* 集合包含某几个元素,譬如可以查询User类中Set set包含"ABC","bcd"的User集合,
* 或者查询User中Set的Address的name为"北京"的所有User集合
* 集合可以为基本类型或者JavaBean,可以是one to many或者是@ElementCollection
* @param fieldName
* 列名
* @param value
* 集合
* @return
* expresssion
*/
public static LogicalExpression hasMembers(String fieldName, Object... value) {
SimpleExpression[] ses = new SimpleExpression[value.length];
int i = 0;
//集合中对象是基本类型,如Set,List
Criterion.Operator operator = Criterion.Operator.IS_MEMBER;
//集合中对象是JavaBean
if (fieldName.contains(".")) {
operator = Criterion.Operator.EQ;
}
for (Object obj : value) {
ses[i] = new SimpleExpression(fieldName, obj, operator);
i++;
}
return new LogicalExpression(ses, Criterion.Operator.OR);
}
}
假设有个Post的entity,有title,content,count,url等参数,再创建一个PostRepository extends JpaRepository
@Test
public void contextLoads() {
Criteria criteria = new Criteria<>();
criteria.add(Restrictions.like("title", "1", true));
criteria.add(Restrictions.eq("content", "content1", true));
List postList = postRepository.findAll(criteria);
for (Post post : postList) {
System.out.println(post);
}
}
这里就可以比较优雅的创建不同的条件,然后拼接起来查询即可。相比之下要比原生的写法如
studentInfoDao.findAll(new Specification () {
public Predicate toPredicate(Root root,
CriteriaQuery> query, CriteriaBuilder cb) {
Path namePath = root.get("name");
Path nicknamePath = root.get("nickname");
/**
* 连接查询条件, 不定参数,可以连接0..N个查询条件
*/
query.where(cb.like(namePath, "%李%"), cb.like(nicknamePath, "%王%")); //这里可以设置任意条查询条件
return null;
}
}, page);
}
比这种写法好看一点。