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;
/** * 条件接口 * 用户提供条件表达式接口 * * @Class Name Criterion * @Author lee * @Create In 2012-2-8 */
public interface Criterion {
public enum Operator {
EQ, NE, LIKE, GT, LT, GTE, LTE, AND, OR
}
public 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<Criterion> criterions = new ArrayList<>();
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
if (!criterions.isEmpty()) {
List<Predicate> 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.*;
/** * 简单条件表达式 * * @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;
}
public String getFieldName() {
return fieldName;
}
public Object getValue() {
return value;
}
public Operator getOperator() {
return operator;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
Path expression = null;
if (fieldName.contains(".")) {
String[] names = StringUtils.split(fieldName, ".");
expression = root.get(names[0]);
for (int i = 1; i < names.length; i++) {
expression = expression.get(names[i]);
}
} 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<String>) 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);
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;
}
public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
List<Predicate> 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 com.example.dynamic.Criterion.Operator;
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, Operator.EQ);
}
/** * 不等于 */
public static SimpleExpression ne(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) return null;
return new SimpleExpression(fieldName, value, Operator.NE);
}
/** * 模糊匹配 */
public static SimpleExpression like(String fieldName, String value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) return null;
return new SimpleExpression(fieldName, value, 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, Operator.GT);
}
/** * 小于 */
public static SimpleExpression lt(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) return null;
return new SimpleExpression(fieldName, value, Operator.LT);
}
/** * 小于等于 */
public static SimpleExpression lte(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) return null;
return new SimpleExpression(fieldName, value, Operator.GTE);
}
/** * 大于等于 */
public static SimpleExpression gte(String fieldName, Object value, boolean ignoreNull) {
if (ignoreNull && StringUtils.isEmpty(value)) return null;
return new SimpleExpression(fieldName, value, Operator.LTE);
}
/** * 并且 */
public static LogicalExpression and(Criterion... criterions) {
return new LogicalExpression(criterions, Operator.AND);
}
/** * 或者 */
public static LogicalExpression or(Criterion... criterions) {
return new LogicalExpression(criterions, 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, Operator.EQ);
i++;
}
return new LogicalExpression(ses, Operator.OR);
}
}
假设有个Post的entity,有title,content,count,url等参数,再创建一个PostRepository extends JpaRepository
@Test
public void contextLoads() {
Criteria<Post> criteria = new Criteria<>();
criteria.add(Restrictions.like("title", "1", true));
criteria.add(Restrictions.eq("content", "content1", true));
List<Post> postList = postRepository.findAll(criteria);
for (Post post : postList) {
System.out.println(post);
}
}
这里就可以比较优雅的创建不同的条件,然后拼接起来查询即可。相比之下要比原生的写法如
studentInfoDao.findAll(new Specification<StudentInfo> () {
public Predicate toPredicate(Root<StudentInfo> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<String> namePath = root.get("name");
Path<String> nicknamePath = root.get("nickname");
/** * 连接查询条件, 不定参数,可以连接0..N个查询条件 */
query.where(cb.like(namePath, "%李%"), cb.like(nicknamePath, "%王%")); //这里可以设置任意条查询条件
return null;
}
}, page);
}
比这种写法好看一点。