現有如下場景,需要根據A表的check_code字段和B表的store_code、check_result字段組合查詢,A表與B表的關聯關係爲一對多。
爲了簡化查詢參數,我們對查詢參數進行了封裝,抽出了公共的QueryCondition:
public class QueryCondition<T> {
protected int page = 1;
protected int limit = 10;
public QueryCondition() {
};
public QueryCondition(int page, int limit) {
this.page = page;
this.limit = limit;
}
public Specification<T> getWhereClause() {
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
list.add(cb.equal(root.get("status"), Status.STATUS_ACTIVE));
return cb.and(list.toArray(new Predicate[list.size()]));
}
};
}
public Pageable getPageRequest() {
if (page < 1) {
page = 1;
}
return new PageRequest(page - 1, limit);
}
public Criteria buildPageableCriteria(Criteria c) {
int start = (this.getPage() - 1) * this.getLimit();
c.setFirstResult(start);
c.setMaxResults(this.getLimit());
return c;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
}
在公共QueryCondition的基礎上,我們封裝了個性化的AQueryCondition:
public class AQueryCondition extends QueryCondition<A> {
private String checkCode;
private String storeCode;
public String toListKey() {
String key = toCountKey() + "." + page + "." + limit;
try {
return DigestUtils.md5DigestAsHex(key.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
}
return key;
}
public String toCountKey() {
String key = checkCode.hashCode() + "." + storeCode.hashCode();
try {
return DigestUtils.md5DigestAsHex(key.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
}
return key;
}
public Specification<A> getWhereClause() {
return new Specification<A>() {
@Override
public Predicate toPredicate(Root<A> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<A, B> abMap = root.join("bs",
JoinType.LEFT);
List<Predicate> list = new ArrayList<Predicate>();
if (StringUtils.isNotBlank(checkCode)) {
list.add(cb.equal(root.get("checkCode").as(String.class), checkCode));
}
if (StringUtils.isNotBlank(storeCode)) {
list.add(cb.equal(abMap.get("storeCode").as(String.class), storeCode));
list.add(cb.equal(abMap.get("checkResult").as(String.class), "OK"));
}
query.groupBy(root.get("pkg")).orderBy(cb.desc(root.get("createTime")));
return cb.and(list.toArray(new Predicate[list.size()]));
}
};
}
public String getCheckCode() {
return checkCode;
}
public void setCheckCode(String checkCode) {
this.checkCode = checkCode;
}
public String getStoreCode() {
return storeCode;
}
public void setStoreCode(String storeCode) {
this.storeCode = storeCode;
}
}
由於在AQueryCondition我們用到了A和B的左連接關聯查詢,因此需要我們在A中定義和B的一對多關係。
@OneToMany
@JoinColumn(name = "pkg", referencedColumnName = "pkg", insertable = false, updatable = false)
private Collection<B> bs = new ArrayList<B>();
到此,基礎工作做好了,接着我們看如何在Controller、Service、Dao層進行調用。
Controller層我們通過AQueryCondition接收查詢參數:
@RequestMapping("/list.do")
public @ResponseBody PageInfo<ADto> list(AQueryCondition cond) {
Page<A> page = aService.pageFind(cond);
...
long total = appSpiderService.count(cond);
return new PageInfo<ADto>(result, total, cond.getPage(), cond.getLimit());
}
在Service層和Dao層我們加了緩存,這步可選,我們這裏是Service層調用緩存層:
@Autowired
private CachedARepository aRepository;
public Page<A> pageFind(AQueryCondition cond) {
return aRepository.pageFind(cond);
}
緩存層我們調用AQueryCondition的方法進行組合查詢:
public Page<A> pageFind(final AQueryCondition cond) {
return cache.get(key.getListKey(cond.toListKey()), new CacheGetCallback<Page<A>>() {
@Override
public Page<A> getObject() throws Exception {
return aRepository.findAll(cond.getWhereClause(), cond.getPageRequest());
}
@Override
public String[] getCacheGroups(Page<A> o) {
return new String[] { key.getGrpKey("list") };
}
});
}
public long count(AQueryCondition cond) {
return aRepository.findAll(cond.getWhereClause()).size();
}
Dao層我們進行公共方法的繼承和個性化方法的編寫:
@Transactional
public interface ARepository extends JpaRepository<A, Long>, JpaSpecificationExecutor<A> {
}
到此,關於spring data jpa多表多條件查詢的實現步驟已經講解完畢,如何大家在實現過程中有疑問可以找我交流。