spring data jpa 多表多條件查詢

現有如下場景,需要根據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多表多條件查詢的實現步驟已經講解完畢,如何大家在實現過程中有疑問可以找我交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章