本文主要研究下jpa的動態查詢java
jpa從hibernate裏頭吸取了criteria,利用criteria結合對url查詢語法的解析,也能夠實現端到端的動態查詢。
下面展現下springside branch 4版本中的實現。git
/******************************************************************************* * Copyright (c) 2005, 2014 springside.github.io * * Licensed under the Apache License, Version 2.0 (the "License"); *******************************************************************************/ package org.springside.modules.persistence; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Maps; public class SearchFilter { public enum Operator { EQ, LIKE, GT, LT, GTE, LTE } public String fieldName; public Object value; public Operator operator; public SearchFilter(String fieldName, Operator operator, Object value) { this.fieldName = fieldName; this.value = value; this.operator = operator; } /** * searchParams中key的格式爲OPERATOR_FIELDNAME */ public static Map<String, SearchFilter> parse(Map<String, Object> searchParams) { Map<String, SearchFilter> filters = Maps.newHashMap(); for (Entry<String, Object> entry : searchParams.entrySet()) { // 過濾掉空值 String key = entry.getKey(); Object value = entry.getValue(); if (StringUtils.isBlank((String) value)) { continue; } // 拆分operator與filedAttribute String[] names = StringUtils.split(key, "_"); if (names.length != 2) { throw new IllegalArgumentException(key + " is not a valid search filter name"); } String filedName = names[1]; Operator operator = Operator.valueOf(names[0]); // 建立searchFilter SearchFilter filter = new SearchFilter(filedName, operator, value); filters.put(key, filter); } return filters; } }
這裏定義了幾種運算符EQ, LIKE, GT, LT, GTE, LTE
能夠本身從mvc的controller中接收查詢條件轉換爲SearchFilter
/******************************************************************************* * Copyright (c) 2005, 2014 springside.github.io * * Licensed under the Apache License, Version 2.0 (the "License"); *******************************************************************************/ package org.springside.modules.persistence; import java.util.Collection; import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.apache.commons.lang3.StringUtils; import org.springframework.data.jpa.domain.Specification; import org.springside.modules.utils.Collections3; import com.google.common.collect.Lists; public class DynamicSpecifications { public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> filters, final Class<T> entityClazz) { return new Specification<T>() { @Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { if (Collections3.isNotEmpty(filters)) { List<Predicate> predicates = Lists.newArrayList(); for (SearchFilter filter : filters) { // nested path translate, 如Task的名爲"user.name"的filedName, 轉換爲Task.user.name屬性 String[] names = StringUtils.split(filter.fieldName, "."); Path expression = root.get(names[0]); for (int i = 1; i < names.length; i++) { expression = expression.get(names[i]); } // logic operator switch (filter.operator) { case EQ: predicates.add(builder.equal(expression, filter.value)); break; case LIKE: predicates.add(builder.like(expression, "%" + filter.value + "%")); break; case GT: predicates.add(builder.greaterThan(expression, (Comparable) filter.value)); break; case LT: predicates.add(builder.lessThan(expression, (Comparable) filter.value)); break; case GTE: predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value)); break; case LTE: predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value)); break; } } // 將全部條件用 and 聯合起來 if (!predicates.isEmpty()) { return builder.and(predicates.toArray(new Predicate[predicates.size()])); } } return builder.conjunction(); } }; } }
這裏主要是將SearchFilter構造的查詢條件轉換爲對應的Predicate,進而構形成jpa的Specification,來完成動態查詢條件的轉換。
使用springside的DynamicSpecifications,再把mvc的參數映射爲SearchFilter,也能夠本身實現一套端到端的動態查詢。github
備註springside在最新版本已經刪除掉這些代碼,得在branch 4分支查找。