封裝Specification查詢條件,在Spring Data JPA 2.0以前使用 Specifications 這個輔助類來操作where、not、and和or連接,在2.0版本以后這個類會被剔除,可以直接使用 Specification 自身對象來操作where多條件連接。(以下展示單表多條件查詢)
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * SQL拼接工具類 * * @author yanhu * @date 2018/8/9 */ public class SpecificationFactory { private Specifications specs; private SpecificationFactory(Specification specs) { this.specs = Specifications.where(specs); } public static SpecificationFactory wheres(Specification spec) { return new SpecificationFactory(spec); } public SpecificationFactory and(Specification other) { this.specs.and(other); return this; } public SpecificationFactory or(Specification other) { this.specs.or(other); return this; } public Specifications build() { return this.specs; } /** * 單where條件 * * @param p * @return */ public static Specification where(Predication p) { List<Predication> ps = new ArrayList<>(); ps.add(p); return where(ps); } /** * 多where條件and連接 * * @param ps * @param <T> * @return */ public static <T> Specification<T> where(List<Predication> ps) { return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> builder.and(getPredicateList(root, builder, ps)); } /** * 多where條件or連接 * * @param ps * @param <T> * @return */ public static <T> Specification<T> or(List<Predication> ps) { return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) -> builder.or(getPredicateList(root, builder, ps)); } /** * 獲取查詢條件數組 * * @param root * @param builder * @param ps * @return */ private static Predicate[] getPredicateList(Root<?> root, CriteriaBuilder builder, List<Predication> ps) { List<Predicate> predicateList = new ArrayList<>(); ps.forEach(p -> { Predicate predicate = buildPredicate(builder, root.get(p.getName()), p); predicateList.add(predicate); }); return predicateList.toArray(new Predicate[predicateList.size()]); } /** * 選取查詢方式 * * @param cb * @param path * @param p * @return */ private static Predicate buildPredicate(CriteriaBuilder cb, Path path, Predication p) { Predicate predicate; switch (p.getOperator()) { case LIKE: predicate = cb.like(path, p.getValue().toString()); break; case EQ: predicate = cb.equal(path, p.getValue()); break; case NOTEQ: predicate = cb.notEqual(path, p.getValue()); break; case GT: predicate = cb.greaterThan(path, (Comparable) p.getValue()); break; case GTEQ: predicate = cb.greaterThanOrEqualTo(path, (Comparable) p.getValue()); break; case LT: predicate = cb.lessThan(path, (Comparable) p.getValue()); break; case LTEQ: predicate = cb.lessThanOrEqualTo(path, (Comparable) p.getValue()); break; case NULL: predicate = cb.isNull(path); break; case NOTNULL: predicate = cb.isNotNull(path); break; case IN: predicate = getIn(path, p.getValue()); break; case NOTIN: predicate = getIn(path, p.getValue()).not(); break; default: throw new IllegalArgumentException("非法的操作符"); } return predicate; } /** * 創建in操作 * * @param path * @param value * @param <T> * @return */ private static <T> Predicate getIn(Path path, T value) { if (value instanceof Object[]) { return path.in((Object[]) value); } else if (value instanceof Collection) { return path.in((Collection) value); } else { throw new IllegalArgumentException("非法的IN操作"); } } /***********************************************單where條件查詢********************************************************/ // like public static Specification like(String name, String value) { return (root, query, cb) -> cb.like(root.get(name), value); } // = public static Specification equal(String name, Object value) { return (root, query, cb) -> cb.equal(root.get(name), value); } // != public static Specification notEqual(String name, Object value) { return (root, query, cb) -> cb.notEqual(root.get(name), value); } // > public static Specification gt(String name, Object value) { return (root, query, cb) -> cb.greaterThan(root.get(name), (Comparable) value); } // >= public static Specification gtEqual(String name, Object value) { return (root, query, cb) -> cb.greaterThanOrEqualTo(root.get(name), (Comparable) value); } // < public static Specification lt(String name, Object value) { return (root, query, cb) -> cb.lessThan(root.get(name), (Comparable) value); } // <= public static Specification ltEqual(String name, Object value) { return (root, query, cb) -> cb.lessThanOrEqualTo(root.get(name), (Comparable) value); } // is null public static Specification isNull(String name) { return (root, query, cb) -> cb.isNull(root.get(name)); } // is not null public static Specification notNull(String name) { return (root, query, cb) -> cb.isNotNull(root.get(name)); } // in public static Specification in(String name, Object value) { return (root, query, cb) -> root.get(name).in(value); } // not in public static Specification notIn(String name, Object value) { return (root, query, cb) -> root.get(name).in(value).not(); } }
import lombok.Data; @Data public class Predication<T> { private OP operator; private String name; private T value; private Predication() { } public static <T> Predication<T> get(OP operator, String name, T value) { return new Builder().operator(operator) .name(name).value(value).build(); } public static class Builder<T> { private Predication p; public Builder() { this.p = new Predication(); } public Builder operator(OP op) { this.p.operator = op; return this; } public Builder name(String name) { this.p.name = name; return this; } public Builder value(T value) { this.p.value = value; return this; } public <T> Predication<T> build() { return this.p; } } }
public enum OP { // like LIKE, // = EQ, // != NOTEQ, // > GT, // >= GTEQ, // < LT, // <= LTEQ, // is null NULL, // is not null NOTNULL, // in IN, // not in NOTIN, AND, OR, NOT }
具體使用
Sort sort = new Sort(Sort.Direction.DESC, "id"); Pageable pageable = new PageRequest(number, size, sort); Specification spec; /***********************單條件查詢*************************/ // 方式1 Predication p = Predication.get(OP.EQ, "name", name); spec = SpecificationFactory.where(p); // 方式2 spec = SpecificationFactory.equal("name", name); /***********************多條件查詢*************************/ List<Predication> ps = new ArrayList<>(); ps.add(Predication.get(OP.LIKE, "name", name)); ps.add(Predication.get(OP.EQ, "age", age)); // 全and連接 spec = SpecificationFactory.where(ps); // 全or連接 spec = SpecificationFactory.or(ps); // and和or混合連接 // where name like ?1 and age = ?2 // and name like ?3 and age = ?4 // or name like ?5 or age = ?6 // 工具類實現 spec = SpecificationFactory.wheres(SpecificationFactory.where(ps)) .and(SpecificationFactory.where(ps)) .or(SpecificationFactory.or(ps)) .build(); // JPA API輔助類實現 spec = Specifications.where(SpecificationFactory.where(ps)) .and(SpecificationFactory.where(ps)) .or(SpecificationFactory.where(ps)); // where name like ?1 and age = ?2 // and ( name like ?3 or age = ?4 ) // 工具類實現 spec = SpecificationFactory.wheres(SpecificationFactory.where(ps)) .and(SpecificationFactory.or(ps)) .build(); // JPA API輔助類實現 spec = Specifications.where(SpecificationFactory.where(ps)) .and(SpecificationFactory.or(ps)); Page<ConsultChat> chatPage = consultChatDao.findAll(spec, pageable);