//dao層 繼承 擴展倉庫接口JpaSpecificationExecutor (JPA 2引入了一個標准的API)
public interface CreditsEventDao extends JpaRepository<CreditsEventBean, Integer>, JpaSpecificationExecutor<CreditsEventBean>{}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ 官方文檔 -- 5.5. Specifications!
JpaSpecificationExecutor提供了以下接口:
/** * Interface to allow execution of {@link Specification}s based on the JPA criteria API. * * @author Oliver Gierke */ public interface JpaSpecificationExecutor<T> { /** * Returns a single entity matching the given {@link Specification}. * 返回與給定的{@link規范}匹配的單個實體 * @param spec * @return */ T findOne(Specification<T> spec); /** * Returns all entities matching the given {@link Specification}. * 返回與給定的{@link規范}匹配的所有實體。 * @param spec * @return */ List<T> findAll(Specification<T> spec); /** * Returns a {@link Page} of entities matching the given {@link Specification}. * 返回與給定的{@link規范}匹配的實體的{@link頁面}。 * @param spec * @param pageable * @return */ Page<T> findAll(Specification<T> spec, Pageable pageable); /** * Returns all entities matching the given {@link Specification} and {@link Sort}. * 返回與給定的{@link規范}和{@link排序}匹配的所有實體。 * @param spec * @param sort * @return */ List<T> findAll(Specification<T> spec, Sort sort); /** * Returns the number of instances that the given {@link Specification} will return. * 返回給定的{@link規范}將返回的實例數量。 * @param spec the {@link Specification} to count instances for * @return the number of instances */ long count(Specification<T> spec); }
我項目上使用到的實例:
public Page<TestBean> specificationFind(Integer size, Integer page, TestBean test) { Field[] fields = CreditsEventBean.class.getDeclaredFields(); //通過反射獲取實體類的所有屬性 Sort sort = new Sort(Direction.ASC, "sort").and(new Sort(Direction.DESC, "cutOffTime"));//排序規則 多條件 Pageable pageable = new PageRequest(page-1, size, sort);//分頁 return activityDao.findAll(new Specification<CreditsEventBean>() {// Page<T> findAll(Specification<T> spec, Pageable pageable); 分頁加多態查詢 @SuppressWarnings("unchecked")//壓制警告 @Overridepublic Predicate toPredicate(Root<CreditsEventBean> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Map<String, Object> conditions = null; try { conditions = BeanUtils.describe(event);//使用Apache的工具類將實體轉換成map conditions.remove("sort");//去掉某些用不到的字段 比如排序字段等 conditions.remove("maxPlayers");// } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); System.err.println("specificationFind ---bean轉map出錯"); } List<Predicate> predicates = new ArrayList<>();//存儲查詢語句 for (int i = 0; i < fields.length; i++) {//循環bean的所有屬性 fields[i].setAccessible(true);//是否允許訪問 String name = fields[i].getName();//獲取屬性名 if(conditions.containsKey(name)) {//查詢這個鍵是否存在與這個屬性中 if(ObjectUtils.isEmpty(conditions.get(name))) {//判斷這個鍵的值是否為空 continue;//結束本次循環,進入下一次循環 } if(name.equals("creditStatus")||name.equals("activityShop") ||name.equals("isShopEvent")||name.equals("isPutaway")) {//這里 等於條件 predicates.add(cb.equal(root.get(name), conditions.get(name)));
注意:如果查詢的表中有關聯的表,可能會報錯 原因:可能是bean轉map的時候,BeanUtils.describe方法將關聯bean沒取出來,只是取了關聯bean的內存地址並存儲為字符串,導致關聯bean的數據消失 暴力解決方法: if(name.equals("關聯Bean")) { predicates.add(cb.equal(root.get(name),filed.get關聯Bean())); }else { predicates.add(cb.equal(root.get(name), conditions.get(name))); }
continue; }else if(name.equals("activityStartCreditsDT")||name.equals("activityEndCreditsDT") ||name.equals("creteateTime")||name.equals("cutOffTime")) {// 這里是between條件 String value = (String) conditions.get(name); String[] split = value.split(",");//分割 predicates.add(cb.between(root.get(name), split[0], split[1])); continue; } predicates.add(cb.like(root.get(name), "%"+conditions.get(name)+"%"));// 這里是 模糊 條件 } } return cb.and(predicates.toArray(new Predicate[predicates.size()]));//返回結果集 } }, pageable); }
CriteriaBuilder主要api:
等!