1 首先封装查询对象,前台传来的对象的数据结构为
能实现以下搜索功能
对应后端的类结构为
2 实现的功能:
查询,排序,分页,配置查询后需要显示的字段,可以fetch当前类中fetchType=lazy的字段;
import cn.topcheer.ivms.pojo.search.JpaClassify;
import cn.topcheer.ivms.pojo.search.JpaConditionType;
import cn.topcheer.ivms.pojo.search.JpaPageRequest;
import cn.topcheer.ivms.pojo.search.JpaSort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import javax.persistence.*;
import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;
/**
* jpa搜索工具类
* Created by wmh on 2018/11/12.
*/
public class JpaSearchHelper<T> {
private static final Logger logger = LoggerFactory.getLogger(JpaSearchHelper.class);
/**
* 搜索
* @param jpaPageRequest 搜索的相关条件
* @param clazz 当前查询的类
* @param em EntityManager
* @param fetchFields fetch当前类中fetch=lazy的属性
* @param selectFields 查询后需要显示的字段
* @param <T>
* @return
*/
public static<T> Page search(JpaPageRequest jpaPageRequest,Class<T> clazz,EntityManager em,String fetchFields,String selectFields){
try {
List<Tuple> list = build(em,clazz,jpaPageRequest,fetchFields,selectFields);
return toPage(selectFields,list,jpaPageRequest);
}catch (Exception e){
logger.error("search error:"+e);
return null;
}
}
/**
* 使用entityManager构建查询
* @param em
* @param clazz
* @param jpaPageRequest
* @param fetchFields
* @param selectFields
* @param <T>
* @return
*/
private static <T> List build(EntityManager em, Class clazz, JpaPageRequest jpaPageRequest, String fetchFields, String selectFields) throws Exception {
try {
if (jpaPageRequest != null) {
CriteriaBuilder cb = em.getCriteriaBuilder();
//列表查询
CriteriaQuery criteriaQuery;
if (selectFields != null && !selectFields.isEmpty()) {
criteriaQuery = cb.createTupleQuery();
} else {
criteriaQuery = cb.createQuery(clazz);
}
//from
Root<T> root = criteriaQuery.from(clazz);
//fetch
addFetchFields(root, fetchFields);
//select
List<Selection<?>> selections = addSelectFields(root, selectFields);
if (selections != null && selections.size() > 0) {
criteriaQuery.multiselect(selections);
} else {
criteriaQuery.select(root);
}
//where
Predicate predicate = toPredicate(root, cb, clazz, jpaPageRequest);
if (predicate != null) {
criteriaQuery.where(predicate);
}
//order by
List<Order> orders = addOrders(root, jpaPageRequest.getSorts(), cb);
if (orders != null && orders.size() > 0) {
criteriaQuery.orderBy(orders);
}
criteriaQuery.distinct(true);
TypedQuery query = em.createQuery(criteriaQuery);
query.setFirstResult(jpaPageRequest.getPage() * jpaPageRequest.getSize());
query.setMaxResults(jpaPageRequest.getSize());
return query.getResultList();
}
return null;
}catch (Exception e){
throw new Exception("build search error"+e);
}
}
/**
* @param root
* @param fetchFields
* @param <T>
*/
private static <T> void addFetchFields(Root root,String fetchFields){
if(fetchFields!=null && !fetchFields.isEmpty()) {
String[] splitFetchFields = fetchFields.split(",");
//此处若用root.fetch,分页会出现问题
Join join = root.join(splitFetchFields[0],JoinType.LEFT);
for(int i=1;i<splitFetchFields.length;i++) {
join = join.join(splitFetchFields[i], JoinType.LEFT);
}
}
}
/**
* 添加排序
* @param root
* @param jpaSorts
* @param criteriaBuilder
* @param <T>
* @return
*/
private static <T> List addOrders(Root root, List<JpaSort> jpaSorts, CriteriaBuilder criteriaBuilder){
if(jpaSorts!=null && jpaSorts.size()>0) {
List result = new ArrayList();
for (JpaSort jpaSort : jpaSorts) {
String field = jpaSort.getField();
Path path = getPath(field, root);
if (path != null && jpaSort.getDirection()!=null) {
if (jpaSort.getDirection().equals("ASC")) {
result.add(criteriaBuilder.asc(path));
} else if (jpaSort.getDirection().equals("DESC")) {
result.add(criteriaBuilder.desc(path));
}
}
}
return result;
}
return null;
}
/**
* 添加select所需字段
* 可以在filterFields中添加别名,例如name as 名称...暂未实现
* @param root
* @param selectFields
* @param <T>
*/
private static <T> List addSelectFields(Root root, String selectFields){
List result = new ArrayList();
if(selectFields!=null && !selectFields.isEmpty()) {
String[] splitSelectFields = selectFields.split(",");
for (String field : splitSelectFields) {
Selection selection = getPath(field, root).alias(field);
if (selection != null) {
result.add(selection);
}
}
}
return result;
}
private static<T> Predicate toPredicate(Root root, CriteriaBuilder criteriaBuilder, Class clazz, JpaPageRequest jpaPageRequest){
if(jpaPageRequest!=null) {
Predicate orPredicate = null;
Predicate andPredicate = null;
List<Predicate> predicateOrList = new ArrayList<>();
List<Predicate> predicateAndList = new ArrayList<>();
String filter = jpaPageRequest.getFilter();
List<JpaClassify> jpaClassifyList = jpaPageRequest.getClassifyList();
if (jpaPageRequest.getFilter() != null && !"".equals(filter)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(String.class) && !field.getName().equals("id") && !field.getName().equals("serialVersionUID")) {
Predicate predicate = addPredicate(root, criteriaBuilder, field.getName(), field.getType(), jpaPageRequest.getFilter(), JpaConditionType.LIKE);
if (predicate != null) {
predicateOrList.add(predicate);
}
}
}
//将所有filter条件用 or 联合起来
if (predicateOrList.size() > 0) {
orPredicate = criteriaBuilder.or(predicateOrList.toArray(new Predicate[predicateOrList.size()]));
}
}
if (jpaClassifyList != null && jpaClassifyList.size() > 0) {
for (JpaClassify jpaClassify : jpaClassifyList) {
//暂未用到,预留
Class<?> c = null;
// try {
// c = clazz.getDeclaredField(jpaClassify.getField()).getType();
// } catch (NoSuchFieldException e) {
// e.printStackTrace();
// }
Predicate predicate = addPredicate(root, criteriaBuilder, jpaClassify.getField(), c, jpaClassify.getValue(), jpaClassify.getCondition());
if (predicate != null) {
predicateAndList.add(predicate);
}
}
//将classifies条件用 and 联合起来
if (predicateAndList.size() > 0) {
andPredicate = criteriaBuilder.and(predicateAndList.toArray(new Predicate[predicateAndList.size()]));
}
}
if(andPredicate!=null && orPredicate!=null){
return criteriaBuilder.and(andPredicate,orPredicate);
}else {
if(andPredicate==null && orPredicate!=null){
return orPredicate;
}else if(orPredicate==null && andPredicate!=null){
return andPredicate;
}
}
return null;
}
return null;
}
private static<T> Predicate addPredicate(Root<T> root, CriteriaBuilder criteriaBuilder, String field, Class<?> tClass, Object value, JpaConditionType condition){
Path path = getPath(field,root);
if(path!=null && value!=null) {
switch (condition) {
case LIKE:
return criteriaBuilder.like(path, "%" + value + "%");
case EQUAL:
return criteriaBuilder.equal(path, value);
case NOT_EQUATE:
return criteriaBuilder.notEqual(path, value);
case GREATER_THAN:
//日期类型未测试
return criteriaBuilder.greaterThan(path, (Comparable) value);
case GREATER_THAN_OR_EQUAL_TO:
return criteriaBuilder.greaterThanOrEqualTo(path, (Comparable) value);
case LESS_THAN:
return criteriaBuilder.lessThan(path, (Comparable) value);
case LESS_THAN_OR_EQUAL_TO:
return criteriaBuilder.lessThanOrEqualTo(path, (Comparable) value);
// if(tClass.equals(Date.class)){
// Date date = null;
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// try {
// date = sdf.parse(value);
// } catch (ParseException e) {
// e.printStackTrace();
// }
// return criteriaBuilder.lessThanOrEqualTo(root.get(field).as(Date.class),date);
// }else {
// return criteriaBuilder.lessThanOrEqualTo(root.get(field).as(Double.class),Double.parseDouble(value));
// }
}
}
return null;
}
private static <T> Path getPath(String field,Root root){
if(field!=null) {
String[] fields = field.split("\\.");
Path path = null;
if (fields.length > 1) {
Join join = null;
Set<Join<T, ?>> joinSet = root.getJoins();
//重要:检查join是否已经存在,存在的话就不需要join
String joinName = fields[fields.length - 2];
boolean joinFlag = false;
Iterator iterator = joinSet.iterator();
while (iterator.hasNext()) {
join = (Join) iterator.next();
if (joinName.equals(join.getAttribute().getName())) {
joinFlag = true;
break;
}
}
if (!joinFlag) {
join = root.join(fields[0], JoinType.LEFT);
for (int i = 1; i < fields.length - 1; i++) {
join = join.join(fields[i], JoinType.LEFT);
}
}
path = join.get(fields[fields.length - 1]);
} else {
path = root.get(fields[0]);
}
return path;
}
return null;
}
private static <T> Page toPage(String selectFields,List<Tuple> list,JpaPageRequest jpaPageRequest){
Sort sort = jpaPageRequest.generateSort();
PageRequest pageRequest = new PageRequest(jpaPageRequest.getPage(), jpaPageRequest.getSize(), sort);
Page results = null;
if(selectFields!=null && !selectFields.isEmpty()) {
//将查询结果转成List<map>
List<Map<String, Object>> dataList = new ArrayList<>();
if (list != null && !list.isEmpty()) {
for (Tuple tu : list) {
Map<String, Object> itemmap = new HashMap<>();
for (TupleElement element : tu.getElements()) {
try {
itemmap.put(element.getAlias(), tu.get(element.getAlias()));
} catch (Exception e) {
e.printStackTrace();
}
}
dataList.add(itemmap);
}
}
results = new PageImpl<>(dataList,pageRequest,jpaPageRequest.getSize());
} else {
results = new PageImpl<>(list,pageRequest,jpaPageRequest.getSize());
}
return results;
}
}
3 难点
(1)生成sql时,语句中会有多余的letf join代码,在查看Specification的实现时,发现可以对join进行检查,当join已经存在时,使用已有的join不要添加新的join,具体代码在getPath方法中
(2)使用fetch时,分页出现错误,root.fetch的实现使用join实现,尝试使用root.join实现fetch的功能,发现可以解决分页问题,注意要去除重复项criteriaQuery.distinct(true);
扫描二维码关注公众号,回复:
12651651 查看本文章
![](/qrcode.jpg)