單表CRUD通用分頁service方法抽冉粑洹(MP、JPA支持)

1. ORM單表CRUD

在流行的JAVA ORM框架中有Mybatis以及JPA玉组,其中JPA提供了JpaRepository JpaSpecificationExecutor等接口谎柄,提供了大量單表基礎(chǔ)CRUD方法。而在Mybatis中惯雳,雖然官方提供的功能有限朝巫,但實(shí)際的第三方插件例如Mybatis-Plus 、通用Mapper等也提供極其豐富的方法吨凑。

以Jpa為例捍歪,一個繼承JpaRepository JpaSpecificationExecutor接口的BaseRepository就包含如下方法

image.png

2. 單表復(fù)雜分頁列表需求

如下圖户辱,我們常見的一個分頁列表需求


image.png

分析請求參數(shù)如下:

  • 分頁參數(shù):起始頁,頁大小
  • 查詢條件參數(shù):屬性名糙臼,屬性值庐镐,邏輯條件
  • 排序參數(shù) :屬性名,排序類型

返回參數(shù):

  • 分頁統(tǒng)計數(shù)據(jù):當(dāng)前頁变逃,頁大小必逆,記錄數(shù) ,頁數(shù)
  • 數(shù)據(jù)列表

根據(jù)上述需求揽乱,可以發(fā)現(xiàn)名眉,這類單表的高級分頁列表查詢的需求很常見,JPA提供的抽象方法均可組合實(shí)現(xiàn)其功能凰棉,但沒有提供一個“萬能”的方法损拢。

3. 單表分頁列表方法實(shí)現(xiàn)思路

  • 提供自定義請求參數(shù)的封裝,如下


    image.png
  • 提供自定義統(tǒng)一分頁返回數(shù)據(jù)


    image.png

    注意:其中返回數(shù)據(jù)UserDTO為泛型數(shù)據(jù)

  • 根據(jù)實(shí)際項目框架撒犀,開發(fā)BaseController BaseService BaseServiceImpl BaseRepository等基礎(chǔ)MVC類福压,并添加Page方法,Page分頁邏輯在BaseServiceImpl中實(shí)現(xiàn)或舞,具體代碼如下

package com.tba.sc.common.base.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.kichun.common.base.assembler.BaseAssembler;
import com.kichun.common.base.dto.BaseDTO;
import com.kichun.common.base.dto.page.EnumOperateType;
import com.kichun.common.base.dto.page.PageParameter;
import com.kichun.common.base.dto.page.PageResponse;
import com.kichun.common.base.entity.BaseEntity;
import com.kichun.common.base.repository.BaseRepository;
import com.kichun.common.base.service.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.JpaSort;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class BaseServiceImpl<D extends BaseDTO, E extends BaseEntity, ID extends Serializable> implements BaseService<D, E, ID> {
    @Autowired
    private BaseRepository<E, ID> baseRepository;

    @Autowired
    private BaseAssembler<D, E> baseAssembler;

    @Override
    public PageResponse<D> page(PageParameter pageParam) {
        //初始化當(dāng)前頁及頁大小參數(shù)
        Integer page = ObjectUtil.isNotNull(pageParam) ? ObjectUtil.isNotNull(pageParam.getPageidx()) ? pageParam.getPageidx() > 0 ? pageParam.getPageidx() : 0 : 0 : 0;
        Integer size = ObjectUtil.isNotNull(pageParam) ? ObjectUtil.isNotNull(pageParam.getPagesize()) ? pageParam.getPagesize() > 0 ? pageParam.getPagesize() : 10 : 10 : 10;

        //將自定義排序SORT參數(shù)封裝為JPA Sort.Order對象
        List<Sort.Order> orders = new ArrayList<>();
        if (CollUtil.isNotEmpty(pageParam.getSort())) {
            pageParam.getSort().forEach(sort -> {
                if (Sort.Direction.DESC.toString().equals(sort.getSortType())) {
                    orders.add(Sort.Order.desc(sort.getField()));
                } else {
                    orders.add(Sort.Order.asc(sort.getField()));
                }
            });
        }
        //根據(jù)當(dāng)前頁荆姆,頁大小,排序參數(shù)封裝JPA PageRequest對象
        PageRequest pageRequest = PageRequest.of(page, size, JpaSort.by(orders));
        Page<E> pageResult = null;

        //封裝查詢條件
        if (CollUtil.isNotEmpty(pageParam.getQuery())) {
            pageResult = baseRepository.findAll(new Specification<E>() {
                @Override
                public Predicate toPredicate(Root<E> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicates = new ArrayList();
                    pageParam.getQuery().forEach(query -> {
                        if (StrUtil.isBlank(query.getField()) || StrUtil.isBlank(query.getValue()) || StrUtil.isBlank(query.getOperateType())) {
                            return;
                        }
                        if (EnumOperateType.EQ.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.equal(root.get(query.getField()), query.getValue()));
                        } else if (EnumOperateType.GT.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.gt(root.get(query.getField()), Long.valueOf(query.getValue())));
                        } else if (EnumOperateType.LT.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.lt(root.get(query.getField()), Long.valueOf(query.getValue())));
                        } else if (EnumOperateType.NOT_EQ.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.notEqual(root.get(query.getField()), query.getValue()));
                        } else if (EnumOperateType.LIKE.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.like(root.get(query.getField()).as(String.class), "%" + query.getValue() + "%"));
                        } else if (EnumOperateType.IN.toString().equals(query.getOperateType())) {
                            String[] split = query.getValue().split(",");
                            if (ArrayUtil.isNotEmpty(split)) {
                                predicates.add(criteriaBuilder.in(root.get(query.getField()).in(split)));
                            }
                        }
                        if (query.isOr()) {
                            criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
                        } else {
                            criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                        }
                    });
                    return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                }
            }, pageRequest);
        } else {
            pageResult = baseRepository.findAll(pageRequest);
        }

        //封裝自定義分頁返回參數(shù)
        PageResponse<D> pageResponse = new PageResponse<>();
        pageResponse.setPageIndex(new Long(pageResult.getPageable().getPageNumber()));
        pageResponse.setTotal(new Long(pageResult.getTotalElements()));
        pageResponse.setTotalPage(new Long(pageResult.getTotalPages()));
        if (CollUtil.isNotEmpty(pageResult.getContent())) {
            pageResponse.setPageData(baseAssembler.toDTOs(pageResult.getContent()));
        }
        return pageResponse;
    }
}

4. 參數(shù)實(shí)體代碼

EnumOperateType

public enum EnumOperateType {
    EQ,
    NOT_EQ,
    LIKE,
    GT,
    LT,
    IN,
    NOT_IN;
    EnumOperateType() {
    }
}

PageParameter

@Data
public class PageParameter implements Serializable {

    @ApiModelProperty(value = "請求頁,默認(rèn)為0")
    private Integer pageidx = 0;

    @ApiModelProperty(value = "分頁大小映凳,默認(rèn)為10")
    private Integer pagesize = 10;

    @ApiModelProperty(value = "查詢參數(shù)")
    private List<QueryParam> query;

    @ApiModelProperty(value = "排序參數(shù)")
    private List<SortParam> sort;
}

QueryParam

/**
 * 查詢參數(shù)dto胆筒,封裝一個查詢的字段,支持且或條件诈豌,支持EQ NOT_EQ LIKE GT LT IN NOT_IN等判斷
 */
@Data
public class QueryParam implements Serializable {

    @ApiModelProperty(value = "查詢條件是否為或仆救,默認(rèn)為且")
    private boolean or;

    @ApiModelProperty(value = "查詢的字段名")
    private String field;

    @ApiModelProperty(value = "匹配類型:EQ 等于,NOT_EQ 不等于,LIKE 模糊查詢,GT大于,LT小于 ,IN在其中,NOT_IN不在其中value為,分隔字符串")
    private String operateType;

    @ApiModelProperty(value = "匹配值")
    private String value;
}

SortParam

@Data
public class SortParam implements Serializable {

    @ApiModelProperty(value = "排序字段")
    private String field;

    @ApiModelProperty(value = "排序類型:ASC DESC 默認(rèn)ASC")
    private String sortType = "ASC";
}

PageResponse

public class PageResponse<T> {

    /**
     * 總頁數(shù)
     * 當(dāng)前頁頁碼
     * 每一頁的數(shù)據(jù)
     */
    private Long total;
    private Long totalPage;
    private Long pageIndex;
    private List<T> pageData;
}

5 效果展示

請求參數(shù)示例


image.png

返回參數(shù)示例


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末队询,一起剝皮案震驚了整個濱河市派桩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚌斩,老刑警劉巖铆惑,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異送膳,居然都是意外死亡员魏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門叠聋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撕阎,“玉大人,你說我怎么就攤上這事碌补÷彩” “怎么了棉饶?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镇匀。 經(jīng)常有香客問我照藻,道長,這世上最難降的妖魔是什么汗侵? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任幸缕,我火速辦了婚禮,結(jié)果婚禮上晰韵,老公的妹妹穿的比我還像新娘发乔。我一直安慰自己,他們只是感情好雪猪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布栏尚。 她就那樣靜靜地躺著,像睡著了一般浪蹂。 火紅的嫁衣襯著肌膚如雪抵栈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天坤次,我揣著相機(jī)與錄音,去河邊找鬼斥赋。 笑死缰猴,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疤剑。 我是一名探鬼主播滑绒,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隘膘!你這毒婦竟也來了疑故?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弯菊,失蹤者是張志新(化名)和其女友劉穎纵势,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體管钳,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钦铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了才漆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牛曹。...
    茶點(diǎn)故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖醇滥,靈堂內(nèi)的尸體忽然破棺而出黎比,到底是詐尸還是另有隱情超营,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布阅虫,位于F島的核電站演闭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏书妻。R本人自食惡果不足惜船响,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躲履。 院中可真熱鬧见间,春花似錦、人聲如沸工猜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篷帅。三九已至史侣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間魏身,已是汗流浹背惊橱。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留箭昵,地道東北人税朴。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像家制,于是被迫代替她去往敵國和親正林。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容