1. 程式人生 > >Spring-Data-JPA中使用Specification實現動態查詢

Spring-Data-JPA中使用Specification實現動態查詢

      最近專案技術選型db框架選擇了使用JPA,剛開始時,使用jpa進行一些單表簡單的查詢非常輕鬆,大家寫的不亦樂乎大笑,後來在遇到多條件動態查詢的業務場景時,發現現有的JpaRepository提供的方法和自己寫@Query已經滿足了不了需求,難不成要對所有的條件和欄位進行判斷,再寫很多個dao方法驚恐?後面查到jpa提供了圍繞Specification這個類的一系列類,來用於實現動態查詢。

       首先,毫無疑問需要定義一個dao介面,這個介面除了繼承JpaRepository之外,還需要繼承JpaSpecificationExecutor。

public interface FileInfoDao extends JpaRepository<FileInfo, Long>, JpaSpecificationExecutor<FileInfo>{
}

       這是我用於查詢檔案資訊的dao,我們可以看到JpaSpecificationExecutor有以下這些方法:

       T findOne(Specification<T> spec);
	List<T> findAll(Specification<T> spec);
	Page<T> findAll(Specification<T> spec, Pageable pageable);
	List<T> findAll(Specification<T> spec, Sort sort);
	long count(Specification<T> spec);

findOne(spec):根據條件查詢獲取一條資料;

findAll(spec) :根據條件查詢獲取全部資料;

findAll(specification, pageable) : 根據條件分頁查詢資料,pageable設定頁碼、一頁資料量,同時返回的是Page類物件,可以通過getContent()方法拿到List集合資料;

findAll(specification, sort) : 根據條件查詢並返回排序後的資料;

count(spec) : 獲取滿足當前條件查詢的資料總數;

       現在光定義了dao沒用,人家就是需要你往方法裡傳Specification的物件,Specification是一個介面,肯定沒法直接獲取物件。

public interface Specification<T> {
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

        所以我自定義了一個類實現類這個介面。

public class SinoSpecification<T> implements Specification<T> {
	
	private TableQueryParam param;
	
	private Class<T> clazz;
	
	public SinoSpecification(TableQueryParam param, Class<T> clazz) {
		this.param = param;
		this.clazz = clazz;
	}
	
	@Override
	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
		Field[] fields =  clazz.getDeclaredFields();
		Map<String, Object> conditions = param.getCondition();
		List<Predicate> predicates = new ArrayList<>();
		for (int i = 0; i < fields.length; i++) {
			fields[i].setAccessible(true);
			String name = fields[i].getName();
			if(conditions.containsKey(name)) {
				if(ObjectUtils.isEmpty(conditions.get(name))) {
					continue;
				}
				predicates.add(cb.like(root.get(name), "%"+conditions.get(name)+"%"));
			}
		}
		return cb.and(predicates.toArray(new Predicate[predicates.size()]));
	}

}
        TableQueryParam是我自定義的用於封裝查詢條件的類,這個類有一個condition屬性是條件集合。在這個類中,我是通過反射獲取實體類的欄位屬性,再去和TableQueryParam中的conditon裡的條件進行比對,如果條件中包含,則將這個條件以及查詢的值一起構造加入進Predicate,cb.like(arg1, arg2)這個方法,就代表模糊查詢,sql中的Like,arg1引數代表要查詢的欄位,arg2代表查詢的欄位的值,轉換成sql就是 arg1 LIKE arg2。當然,我這個自定義的Specification主要是用於模糊查詢的,而它不僅僅支援模糊,還可以通過CriteriaBuilder裡的方法來實現各種組合查詢,例如cb.equal()進行等於查詢,cb.greaterThan()、cb.lessThan()來實現大於小於,等等,可以說非常豐富。

        定義好Specification之後,就可以呼叫前面說的findAll方法,將自定義的specification物件放進去即可。