springboot jpa 分頁(yè)查詢工具類

1、查詢關(guān)系匹配枚舉

package com.xx.xxx.enums;

/**
 * @description: 查詢條件關(guān)系匹配
 * @author: fangzhao
 * @create: 2020/4/1 15:34
 * @update: 2020/4/1 15:34
 */
public enum MatchCondition {
    /**
     * equal-相等嗦枢,notEqual-不等于攀芯,like-模糊匹配,notLike-文虏,
     * gt-大于侣诺,ge-大于等于,lt-小于氧秘,le-小于等于年鸳,
     * greaterThan-大于,greaterThanOrEqualTo-大于等于丸相,lessThan-小于搔确,lessThanOrEqualTo-小于等于
     */
    EQUAL,
    NOT_EQUAL,
    LIKE,
    NOT_LIKE,

    GT,
    GE,
    LT,
    LE,

    GREATER_THAN,
    GREATER_THAN_OR_EQUAL_TO,
    LESS_THAN,
    LESS_THAN_OR_EQUAL_TO
}

2、查詢條件注解

package com.xx.xxx.annotations;

import com.xx.xxx.enums.MatchCondition;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @description: 查詢條件
 * @author: fangzhao
 * @create: 2020/4/1 15:34
 * @update: 2020/4/1 15:34
 */
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR })
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryCondition {

    /**
     * 數(shù)據(jù)庫(kù)中字段名,默認(rèn)為空字符串,則Query類中的字段要與數(shù)據(jù)庫(kù)中字段一致
     */
    String column() default "";

    /**
     * @see MatchCondition
     */
    MatchCondition func() default MatchCondition.EQUAL;

    /**
     * object是否可以為null
     */
    boolean nullable() default false;

    /**
     * 字符串是否可為空
     */
    boolean emptyable() default false;
}

3灭忠、分頁(yè)工具類

package com.xx.xxx.utils;

import com.xx.xxx.annotations.QueryCondition;
import com.xx.xxx.vo.OrderInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @description: 分頁(yè)和排序工具類
 * @author: fangzhao
 * @create: 2020/3/24 13:09
 * @update: 2020/3/24 13:09
 */
@Slf4j
public class PageUtil<T> {

    /**
     * 描述:排序處理膳算,默認(rèn)id降序
     *
     * @param orderInfos,這里也可以使用可變長(zhǎng)度的參數(shù)列表 OrderInfoVo... 
     * @return org.springframework.data.domain.Sort
     * @author fangzhao at 2020/11/13 9:22
     */
    public static Sort getSortOrder(List<OrderInfoVo> orderInfos) {
        List<Sort.Order> sortOrders = new ArrayList<>();
        if (null == orderInfos || CollectionUtils.isEmpty(orderInfos)) {
            Sort.Order sortOrder = new Sort.Order(Sort.Direction.DESC, "id");
            sortOrders.add(sortOrder);
        } else {
            for (OrderInfoVo order : orderInfos) {
                String orderField = order.getOrderField();
                String orderType = order.getOrderType();
                Sort.Order sortOrder = new Sort.Order("asc".equalsIgnoreCase(orderType) ? Sort.Direction.ASC : Sort.Direction.DESC, orderField);
                sortOrders.add(sortOrder);
            }
        }
        return Sort.by(sortOrders);
    }

    /**
     * 描述:生成查詢條件
     *
     * @param rangeTime
     * @param timeStart
     * @param timeEnd
     * @return org.springframework.data.jpa.domain.Specification<T>
     * @author fangzhao at 2020/11/13 9:24
     */
    public Specification<T> specification(String rangeTime, LocalDateTime timeStart, LocalDateTime timeEnd) {

        return (Specification<T>) (root, criteriaQuery, criteriaBuilder) -> {
            // 增加篩選條件
            Predicate predicate = criteriaBuilder.conjunction();
            // 起始日期
            if (null != timeStart) {
                predicate.getExpressions().add(criteriaBuilder.greaterThanOrEqualTo(root.get(rangeTime).as(LocalDateTime.class), timeStart));
            }
            // 結(jié)束日期
            if (null != timeEnd) {
                predicate.getExpressions().add(criteriaBuilder.lessThan(root.get(rangeTime).as(LocalDateTime.class), timeEnd));
            }
            return predicate;
        };
    }

    /**
     * @description: 根據(jù)注解信息生成查詢條件
     * @creater: fangzhao
     * @updater:
     * @create: 2020/3/24 13:09
     * @update: 2020/3/24 13:09
     * @Param: request entity
     * @return:
     */
    public Specification specification(T request, T entity) {

        List<Field> reqFieldList = getAllFieldsWithRoot(request.getClass());
        Map<T, List<Field>> map = new HashMap<>(16);
        map.put(request, reqFieldList);
        for (Field field : reqFieldList) {
            if (field.getType().equals(entity.getClass())) {
                List<Field> entityFieldList = getAllFieldsWithRoot(field.getType());
                map.put(entity, entityFieldList);
                break;
            }
        }
        return getSpecification(request, map);
    }

    /**
     * 描述:根據(jù)注解信息生成查詢條件
     *
     * @param request
     * @return org.springframework.data.jpa.domain.Specification
     * @author fangzhao at 2020/4/9 10:00
     */
    public Specification specification(T request) {

        List<Field> reqFieldList = getAllFieldsWithRoot(request.getClass());
        Map<T, List<Field>> map = new HashMap<>(4);
        map.put(request, reqFieldList);
        return getSpecification(request, map);
    }

    /**
     * 描述:獲取類clazz的所有Field弛作,包括其父類的Field
     *
     * @param clazz
     * @return java.util.List<java.lang.reflect.Field>
     * @author fangzhao at 2020/4/9 10:00
     */
    private List<Field> getAllFieldsWithRoot(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        Field[] dFields = clazz.getDeclaredFields();
        if (ArrayUtils.isNotEmpty(dFields)) {
            fieldList.addAll(Arrays.asList(dFields));
        }

        // 若父類是Object涕蜂,則直接返回當(dāng)前Field列表
        Class<?> superClass = clazz.getSuperclass();
        if (Object.class == superClass) {
            return Arrays.asList(dFields);
        }

        // 遞歸查詢父類的field列表
        List<Field> superFields = getAllFieldsWithRoot(superClass);

        if (!CollectionUtils.isEmpty(superFields)) {
            superFields.stream().
                    filter(field -> !fieldList.contains(field)).
                    forEach(field -> fieldList.add(field));
        }
        return fieldList;
    }

    /**
     * 描述:生成查詢條件
     *
     * @param request
     * @param map
     * @return org.springframework.data.jpa.domain.Specification
     * @author fangzhao at 2020/4/9 9:59
     */
    private Specification getSpecification(T request, Map<T, List<Field>> map) {
        Specification<T> specification = new Specification<T>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Predicate predicate = criteriaBuilder.conjunction();
                if (null == map || CollectionUtils.isEmpty(map)) {
                    return predicate;
                }
                Set objects = map.keySet();
                for (Object obj : objects) {
                    List<Field> fieldList = map.get(obj);
                    for (Field field : fieldList) {
                        QueryCondition qc = field.getAnnotation(QueryCondition.class);
                        if (null == qc) {
                            continue;
                        }
                        // 如果主注解上colume為默認(rèn)值"",則以field為準(zhǔn)
                        String column = StringUtils.isNotBlank(qc.column()) ? qc.column() : field.getName();
                        field.setAccessible(true);

                        Object value = null;
                        // 默認(rèn)getter方法獲取屬性值映琳,如果是boolean基本類型宇葱,設(shè)置為is
                        String getValueType = "boolean".equals(field.getGenericType().toString()) ? "is" : "get";
                        char[] chars = field.getName().toCharArray();
                        chars[0] -= 32;
                        try {
                            Method method = obj.getClass().getMethod(getValueType + new String(chars));
                            value = method.invoke(obj);
                        } catch (Exception e) {
                            log.error("dataMiddle tools: {}", e.getMessage());
                        }
                        // 如果值為null,且注解未標(biāo)注nullable刊头,跳過(guò)
                        if (null == value && !qc.nullable()) {
                            continue;
                        }
                        if (null != value && String.class.isAssignableFrom(value.getClass())) {
                            String s = (String) value;
                            // 如果值為""黍瞧,且注解未標(biāo)注emptyable怀跛,跳過(guò)
                            if ("".equals(s) && !qc.emptyable()) {
                                continue;
                            }
                        }
                        // 通過(guò)注解上func屬性吸耿,構(gòu)建路徑表達(dá)式
                        Path path = null;
                        try {
                            path = root.get(column);
                        } catch (IllegalArgumentException e) {
                            log.error("dataMiddle tools: {}", e.getMessage());
                        }
                        if (null != path) {
                            switch (qc.func()) {
                                case EQUAL:
                                    predicate.getExpressions().add(criteriaBuilder.equal(path, value));
                                    break;
                                case LIKE:
                                    predicate.getExpressions().add(criteriaBuilder.like(path, "%" + value + "%"));
                                    break;
                                case GE:
                                    if (value instanceof LocalDateTime) {
                                        predicate.getExpressions().add(criteriaBuilder.greaterThanOrEqualTo(path.as(LocalDateTime.class), (LocalDateTime) value));
                                    }
                                    break;
                                case LT:
                                    if (value instanceof LocalDateTime) {
                                        predicate.getExpressions().add(criteriaBuilder.lessThan(path.as(LocalDateTime.class), (LocalDateTime) value));
                                    }
                                    break;
                                default:
                            }
                        }
                    }
                }
                return predicate;
            }
        };
        return specification;
    }
}

4早抠、使用

public Page<DataStandardTypeEntity> getPage(DataStandardTypeReqVO request) {

        Sort sortOrder = PageUtil.getSortOrder(request.getOrderInfo());
        Pageable pageable = PageRequest.of(request.getPageNum() - 1, request.getPageSize(), sortOrder);
        Specification<DataStandardTypeEntity> specification = new PageUtil<>().specification(request, request.getEntity());
        return dataStandardTypeRepository.findAll(specification, pageable);
}

DataStandardTypeReqVO 字段添加響應(yīng)的注解

package com.xx.xxx.vo;

import com.yss.datamiddle.po.DataStandardTypeEntity;

/**
 * @description: 數(shù)據(jù)標(biāo)準(zhǔn)類型請(qǐng)求類
 * @author: fangzhao
 * @create: 2020/3/24 13:09
 * @update: 2020/3/24 13:09
 */
public class DataStandardTypeReqVO extends RangeRequest{

    private DataStandardTypeEntity entity = new DataStandardTypeEntity();

    public DataStandardTypeEntity getEntity() {
        return entity;
    }

    public void setEntity(DataStandardTypeEntity entity) {
        this.entity = entity;
    }
}
package com.xxx.xxx.vo;

import com.xxx.xxx.annotations.QueryCondition;
import com.xxx.xxx.enums.MatchCondition;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName RangeRequest
 * @Description 請(qǐng)求范圍
 * @Author fangzhao
 * @Date 2020/3/18 16:07
 */
public class RangeRequest<T> extends PageInfo {

    @ApiModelProperty(value = "創(chuàng)建時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.ge, column = "createTime")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTimeStart;

    @ApiModelProperty(value = "創(chuàng)建時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.lt, column = "createTime")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTimeEnd;

    @ApiModelProperty(value = "修改時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.ge, column = "modifyTime")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime modifyTimeStart;

    @ApiModelProperty(value = "修改時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.lt, column = "modifyTime")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime modifyTimeEnd;

    @ApiModelProperty(value = "有效期開始時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.ge, column = "validDate")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime validDateStart;

    @ApiModelProperty(value = "有效期截止時(shí)間", example = "2020-03-23 14:40:10", hidden = true)
    @QueryCondition(func = MatchCondition.lt, column = "validDate")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime validDateEnd;

    @ApiModelProperty("排序信息")
    private List<OrderInfoVo> orderInfo = new ArrayList<>();
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吻贿,一起剝皮案震驚了整個(gè)濱河市遣耍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逻谦,老刑警劉巖刷晋,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異矢否,居然都是意外死亡仲闽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門僵朗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赖欣,“玉大人,你說(shuō)我怎么就攤上這事验庙《ニ保” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵粪薛,是天一觀的道長(zhǎng)悴了。 經(jīng)常有香客問(wèn)我,道長(zhǎng)违寿,這世上最難降的妖魔是什么湃交? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮藤巢,結(jié)果婚禮上巡揍,老公的妹妹穿的比我還像新娘。我一直安慰自己菌瘪,他們只是感情好腮敌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俏扩,像睡著了一般糜工。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上录淡,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天捌木,我揣著相機(jī)與錄音,去河邊找鬼嫉戚。 笑死刨裆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彬檀。 我是一名探鬼主播帆啃,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窍帝!你這毒婦竟也來(lái)了努潘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疯坤,沒(méi)想到半個(gè)月后报慕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡压怠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年眠冈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菌瘫。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜗顽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出突梦,到底是詐尸還是另有隱情,我是刑警寧澤羽利,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布宫患,位于F島的核電站,受9級(jí)特大地震影響这弧,放射性物質(zhì)發(fā)生泄漏娃闲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一匾浪、第九天 我趴在偏房一處隱蔽的房頂上張望皇帮。 院中可真熱鬧,春花似錦蛋辈、人聲如沸属拾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)渐白。三九已至,卻和暖如春逞频,著一層夾襖步出監(jiān)牢的瞬間纯衍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工苗胀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留襟诸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓基协,卻偏偏與公主長(zhǎng)得像歌亲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子澜驮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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