亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java?Web開(kāi)發(fā)中的分頁(yè)與參數(shù)校驗(yàn)舉例詳解

 更新時(shí)間:2025年02月27日 09:58:31   作者:sjsjsbbsbsn  
這篇文章主要介紹了JavaWeb開(kāi)發(fā)中的分頁(yè)設(shè)計(jì)和參數(shù)校驗(yàn),分頁(yè)設(shè)計(jì)通過(guò)分頁(yè)查詢(xún)參數(shù)優(yōu)化查詢(xún)性能,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在 Java Web 開(kāi)發(fā)中,分頁(yè)和參數(shù)校驗(yàn)是兩個(gè)非常重要的功能。本文將圍繞 分頁(yè)設(shè)計(jì) 和 參數(shù)校驗(yàn) 進(jìn)行探討,包括如何設(shè)計(jì)合理的分頁(yè)查詢(xún)參數(shù),以及如何利用 Java 注解 實(shí)現(xiàn)參數(shù)校驗(yàn)。

分頁(yè)設(shè)計(jì)

為什么需要分頁(yè)?

當(dāng)數(shù)據(jù)庫(kù)表數(shù)據(jù)量較大時(shí),如果直接查詢(xún)所有數(shù)據(jù),可能會(huì)導(dǎo)致 查詢(xún)緩慢,甚至造成 內(nèi)存溢出(OOM)。分頁(yè)是一種常見(jiàn)的優(yōu)化方式,可以 減少數(shù)據(jù)庫(kù)負(fù)載 并 提升前端渲染速度。

如何設(shè)計(jì)分頁(yè)查詢(xún)參數(shù)?

分頁(yè)通常包含以下幾個(gè)核心參數(shù):

  • pageNo:當(dāng)前頁(yè)碼,默認(rèn)為 1。
  • pageSize:每頁(yè)返回的記錄數(shù),默認(rèn)為 20。
  • sortBy:排序字段,如 idcreate_time。
  • isAsc:是否升序,默認(rèn)為 true

我們可以設(shè)計(jì)一個(gè)公共的父類(lèi)PageQuery來(lái)幫助提供默認(rèn)的參數(shù),同時(shí)我們?cè)陂_(kāi)發(fā)中也會(huì)用到mybatisplus,提供出轉(zhuǎn)成page對(duì)象的方法

@Data
@ApiModel(description = "分頁(yè)請(qǐng)求參數(shù)")
@Accessors(chain = true)
public class PageQuery {
    public static final Integer DEFAULT_PAGE_SIZE = 20;
    public static final Integer DEFAULT_PAGE_NUM = 1;

    @ApiModelProperty(value = "頁(yè)碼", example = "1")
    @Min(value = 1, message = "頁(yè)碼不能小于1")
    private Integer pageNo = DEFAULT_PAGE_NUM;

    @ApiModelProperty(value = "每頁(yè)大小", example = "5")
    @Min(value = 1, message = "每頁(yè)查詢(xún)數(shù)量不能小于1")
    private Integer pageSize = DEFAULT_PAGE_SIZE;

    @ApiModelProperty(value = "是否升序", example = "true")
    private Boolean isAsc = true;

    @ApiModelProperty(value = "排序字段", example = "id")
    private String sortBy;

    public int from(){
        return (pageNo - 1) * pageSize;
    }

    public <T> Page<T> toMpPage(OrderItem ... orderItems) {
        Page<T> page = new Page<>(pageNo, pageSize);
        // 是否手動(dòng)指定排序方式
        if (orderItems != null && orderItems.length > 0) {
            for (OrderItem orderItem : orderItems) {
                page.addOrder(orderItem);
            }
            return page;
        }
        // 前端是否有排序字段
        if (StringUtils.isNotEmpty(sortBy)){
            OrderItem orderItem = new OrderItem();
            orderItem.setAsc(isAsc);
            orderItem.setColumn(sortBy);
            page.addOrder(orderItem);
        }
        return page;
    }

    public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
        if (StringUtils.isBlank(sortBy)){
            sortBy = defaultSortBy;
            this.isAsc = isAsc;
        }
        Page<T> page = new Page<>(pageNo, pageSize);
        OrderItem orderItem = new OrderItem();
        orderItem.setAsc(this.isAsc);
        orderItem.setColumn(sortBy);
        page.addOrder(orderItem);
        return page;
    }
    public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
        return toMpPage(Constant.DATA_FIELD_NAME_CREATE_TIME, false);
    }
}

設(shè)計(jì)優(yōu)點(diǎn)

  • 默認(rèn)分頁(yè)參數(shù),即使前端未傳分頁(yè)參數(shù),也不會(huì)報(bào)錯(cuò)。
  • 支持排序,可以根據(jù)前端傳遞的 sortBy 和 isAsc 進(jìn)行排序。
  • 與 MyBatis-Plus 兼容,直接轉(zhuǎn)換為 Page<T>,減少重復(fù)代碼。

使用方式

在實(shí)際開(kāi)發(fā)中,我們可以在 Service 層調(diào)用 toMpPage() 方法,將 PageQuery 轉(zhuǎn)換為 MyBatis-Plus 的 Page<T> 對(duì)象。

public Page<User> getUserList(PageQuery query) {
    Page<User> page = query.toMpPage();
    return userMapper.selectPage(page, new QueryWrapper<>());
}

參數(shù)校驗(yàn)的藝術(shù):從基礎(chǔ)校驗(yàn)到深度防御

為什么參數(shù)校驗(yàn)是系統(tǒng)安全的第一道防線?

在實(shí)際開(kāi)發(fā)中,我們常遇到這樣的問(wèn)題:

  • 用戶(hù)輸入手機(jī)號(hào)為"1381234abcd"
  • 訂單金額出現(xiàn)負(fù)數(shù)
  • 狀態(tài)字段傳入非法數(shù)值
  • 接口被惡意構(gòu)造異常參數(shù)攻擊

參數(shù)校驗(yàn)如同系統(tǒng)的門(mén)衛(wèi),負(fù)責(zé):

  • 攔截80%以上的常規(guī)攻擊
  • 保證業(yè)務(wù)數(shù)據(jù)的有效性
  • 提高代碼可讀性和健壯性
  • 降低下游服務(wù)的校驗(yàn)壓力

JSR 380規(guī)范的核心武器庫(kù)

基礎(chǔ)校驗(yàn)實(shí)戰(zhàn)

@PostMapping("/create")
public Result createCoupon(@Valid @RequestBody CouponFormDTO dto) {
    // 業(yè)務(wù)邏輯
}

@Data
public class CouponFormDTO {
    @NotNull(message = "優(yōu)惠券類(lèi)型不能為空")
    private Integer couponType;
    
    @Range(min=1, max=10, message="限領(lǐng)數(shù)量超出范圍")
    private Integer limitCount;
    
    @EnumValid(enumeration = {0,1}, message="領(lǐng)取方式非法")
    private ReceiveEnums receiveType;
}

常用注解矩陣:

注解適用類(lèi)型適用場(chǎng)景
@NotNull任意對(duì)象確保字段不能為空
@NotBlankString確保字符串不能為空
@NotEmpty集合/數(shù)組確保列表有數(shù)據(jù)
@SizeString/集合限制長(zhǎng)度或元素?cái)?shù)量
@Min/@Max數(shù)值類(lèi)型限制最小/最大值
@PatternString正則表達(dá)式校驗(yàn)
@EmailString郵箱格式校驗(yàn)
@FutureDate時(shí)間必須是未來(lái)時(shí)間
@PastDate時(shí)間必須是過(guò)去時(shí)間
@Digits數(shù)值類(lèi)型限制整數(shù)位數(shù)和小數(shù)位數(shù)

深度解析參數(shù)校驗(yàn)原理

JSR 380 校驗(yàn)流程

  • HTTP 請(qǐng)求進(jìn)入 Controller 層,綁定參數(shù)到 DTO 對(duì)象。
  • **@Valid** 觸發(fā)校驗(yàn)機(jī)制,調(diào)用 Hibernate Validator。
  • 執(zhí)行校驗(yàn)邏輯,遍歷 DTO 字段并檢查注解規(guī)則。
  • 校驗(yàn)失敗拋出 **MethodArgumentNotValidException**。
  • 全局異常處理器捕獲異常,封裝并返回錯(cuò)誤信息。

@Valid vs. @Validated 區(qū)別

特性@Valid@Validated
作用范圍單個(gè) DTODTO + 分組校驗(yàn)
分組支持不支持支持
適用場(chǎng)景基礎(chǔ)校驗(yàn)復(fù)雜業(yè)務(wù)場(chǎng)景
@PostMapping("/update")
public Result update(@Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
    // 業(yè)務(wù)邏輯處理
}

自定義枚舉校驗(yàn)的黑科技

數(shù)據(jù)庫(kù)設(shè)計(jì)的隱痛

我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)時(shí)通常會(huì)使用某個(gè)數(shù)字來(lái)代表某個(gè)狀態(tài),如:

CREATE TABLE coupon (
    status TINYINT COMMENT '0-未激活 1-已生效 2-已過(guò)期'
)

傳統(tǒng)校驗(yàn)方式:

if (!Arrays.asList(0,1,2).contains(status)) {
    throw new IllegalArgumentException();
}

缺陷

  • 校驗(yàn)邏輯分散
  • 可維護(hù)性差
  • 無(wú)法復(fù)用

自定義注解解決方案

定義枚舉校驗(yàn)注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
    int[] value() default {};
    String message() default "非法枚舉值";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

實(shí)現(xiàn)校驗(yàn)邏輯

public class EnumValidator implements ConstraintValidator<EnumValid, Integer> {
    private Set<Integer> allowedValues = new HashSet<>();

    @Override
    public void initialize(EnumValid constraintAnnotation) {
        Arrays.stream(constraintAnnotation.value())
              .forEach(allowedValues::add);
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        if (value == null) return true;
        return allowedValues.contains(value);
    }
}

原理深度解析

ConstraintValidator生命周期

  • 初始化:讀取注解配置
  • 校驗(yàn)時(shí):執(zhí)行isValid方法
  • 結(jié)果處理:返回布爾值

JSR 380規(guī)范實(shí)現(xiàn)要點(diǎn)

  • 校驗(yàn)器發(fā)現(xiàn)機(jī)制:SPI方式加載
  • 級(jí)聯(lián)校驗(yàn):支持對(duì)象嵌套校驗(yàn)
  • 分組校驗(yàn):實(shí)現(xiàn)不同場(chǎng)景的校驗(yàn)規(guī)則

復(fù)雜校驗(yàn)的終極方案

有時(shí),僅僅靠我們的校驗(yàn)比較復(fù)雜,這時(shí)我們可能需要自己來(lái)編寫(xiě)校驗(yàn)邏輯

我們可以通過(guò)自定義注解+AOP來(lái)幫我們實(shí)現(xiàn)

/**
 * 實(shí)現(xiàn)后在接口訪問(wèn)時(shí)如果接口實(shí)現(xiàn)了這個(gè)接口
 * 會(huì)被自動(dòng)自行接口check進(jìn)行校驗(yàn)
 **/
public interface Checker<T> {

    /**
     * 用于實(shí)現(xiàn)validation不能校驗(yàn)的數(shù)據(jù)邏輯
     */
    default void check(){

    }

    default void check(T data){
    }
}

使用示例

Data
@ApiModel(description = "章節(jié)")
public class CataSaveDTO implements Checker {
    @ApiModelProperty("章、節(jié)、練習(xí)id")
    private Long id;
    @ApiModelProperty("目錄類(lèi)型1:章,2:節(jié),3:測(cè)試")
    @NotNull(message = "")
    private Integer type;
    @ApiModelProperty("章節(jié)練習(xí)名稱(chēng)")
    private String name;
    @ApiModelProperty("章排序,章一定要傳,小節(jié)和練習(xí)不需要傳")
    private Integer index;

    @ApiModelProperty("當(dāng)前章的小節(jié)或練習(xí)")
    @Size(min = 1, message = "不能出現(xiàn)空章")
    private List<CataSaveDTO> sections;

    @Override
    public void check() {
        //名稱(chēng)為空校驗(yàn)
        if(type == CourseConstants.CataType.CHAPTER && StringUtils.isEmpty(name)) {
            throw new BadRequestException(CourseErrorInfo.Msg.COURSE_CATAS_SAVE_NAME_NULL);
        }else if(StringUtils.isEmpty(name)){
            throw new BadRequestException(CourseErrorInfo.Msg.COURSE_CATAS_SAVE_NAME_NULL2);
        }
        //名稱(chēng)長(zhǎng)度問(wèn)題
        if (type == CourseConstants.CataType.CHAPTER && name.length() > 30){
            throw new BadRequestException(CourseErrorInfo.Msg.COURSE_CATAS_SAVE_NAME_SIZE);
        }else if(name.length() > 30) {
            throw new BadRequestException(CourseErrorInfo.Msg.COURSE_CATAS_SAVE_NAME_SIZE2);
        }
        if(CollUtils.isEmpty(sections)){
            throw new BadRequestException("不能出現(xiàn)空章");
        }

    }
}

接口方法參數(shù)校驗(yàn)器

/**
 * 接口方法參數(shù)校驗(yàn)器
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ParamChecker {
}

定義切面類(lèi)

@Aspect
@Slf4j
@SuppressWarnings("all")
public class CheckerAspect {

    @Before("@annotation(paramChecker)")
    public void before(JoinPoint joinPoint, ParamChecker paramChecker) {
        Object[] args = joinPoint.getArgs();
        if(ArrayUtils.isNotEmpty(args)){
            //遍歷方法參數(shù),參數(shù)是否實(shí)現(xiàn)了Checker接口
            for (Object arg : args){
                if(arg instanceof Checker) {
                    //調(diào)用check方法,校驗(yàn)業(yè)務(wù)邏輯
                    ((Checker)arg).check();
                }else if(arg instanceof List){
                    //如果參數(shù)是一個(gè)集合也要校驗(yàn)
                    CollUtils.check((List) arg);
                }
            }
        }
    }
}

工具方法

    /**
     * 集合校驗(yàn)邏輯
     *
     * @param data 要校驗(yàn)的集合
     * @param checker 校驗(yàn)器
     * @param <T> 集合元素類(lèi)型
     */
    public static  <T> void  check(List<T> data, Checker<T> checker){
        if(data == null){
            return;
        }
        for (T t : data){
            checker.check(t);
        }
    }

    /**
     * 集合校驗(yàn)邏輯
     *
     * @param data 要校驗(yàn)的集合
     * @param <T> 集合元素類(lèi)型
     */
    public static  <T extends Checker<T>> void  check(List<T> data){
        if(data == null){
            return;
        }
        for (T t : data){
            t.check();
        }
    }

注解使用

  @PostMapping("baseInfo/save")
    @ApiOperation("保存課程基本信息")
    @ParamChecker
    //校驗(yàn)非業(yè)務(wù)限制的字段
    public CourseSaveVO save(@RequestBody @Validated(CourseSaveBaseGroup.class) CourseBaseInfoSaveDTO courseBaseInfoSaveDTO) {
        return courseDraftService.save(courseBaseInfoSaveDTO);
    }

注意:切面類(lèi)沒(méi)有納入ioc容器管理,如果是單體項(xiàng)目加上component注解即可,如果是多模塊項(xiàng)目,使用自動(dòng)裝配功能

總結(jié)

到此這篇關(guān)于Java Web開(kāi)發(fā)中的分頁(yè)與參數(shù)校驗(yàn)舉例詳解的文章就介紹到這了,更多相關(guān)Java Web分頁(yè)與參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java設(shè)計(jì)模式之觀察者模式

    Java設(shè)計(jì)模式之觀察者模式

    本文詳細(xì)講解了Java設(shè)計(jì)模式之觀察者模式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • 使用FileReader采用的默認(rèn)編碼

    使用FileReader采用的默認(rèn)編碼

    這篇文章主要介紹了使用FileReader采用的默認(rèn)編碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java實(shí)現(xiàn)多級(jí)表頭和復(fù)雜表頭的導(dǎo)出功能

    Java實(shí)現(xiàn)多級(jí)表頭和復(fù)雜表頭的導(dǎo)出功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多級(jí)表頭和復(fù)雜表頭的導(dǎo)出功能的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Java2 JDK安裝和配置教程

    Java2 JDK安裝和配置教程

    這篇文章主要為大家詳細(xì)介紹了Java2 JDK安裝和配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • java打印菱形及直角和等腰三角形的方法

    java打印菱形及直角和等腰三角形的方法

    用Java輸出菱形本身是一個(gè)比較簡(jiǎn)單的問(wèn)題,這是Java初學(xué)者都要編寫(xiě)的一個(gè)算法,下面這篇文章主要給大家介紹了關(guān)于java打印菱形及直角和等腰三角形的方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • java實(shí)現(xiàn)兩個(gè)對(duì)象之間傳值及簡(jiǎn)單的封裝

    java實(shí)現(xiàn)兩個(gè)對(duì)象之間傳值及簡(jiǎn)單的封裝

    這篇文章主要介紹了java實(shí)現(xiàn)兩個(gè)對(duì)象之間傳值及簡(jiǎn)單的封裝,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • springboot中如何引入AOP切面編程

    springboot中如何引入AOP切面編程

    這篇文章主要介紹了springboot中如何引入AOP切面編程問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java多線程中的Phaser詳解

    Java多線程中的Phaser詳解

    這篇文章主要介紹了Java多線程中的Phaser詳解,Pahser是一個(gè)可以重復(fù)使用的同步屏障,Phaser是按照不同階段執(zhí)行線程的,它本身維護(hù)著一個(gè)叫 phase 的成員變量代表當(dāng)前執(zhí)行的階段,需要的朋友可以參考下
    2023-11-11
  • 基于Java代碼實(shí)現(xiàn)數(shù)字在數(shù)組中出現(xiàn)次數(shù)超過(guò)一半

    基于Java代碼實(shí)現(xiàn)數(shù)字在數(shù)組中出現(xiàn)次數(shù)超過(guò)一半

    這篇文章主要介紹了基于Java代碼實(shí)現(xiàn)數(shù)字在數(shù)組中出現(xiàn)次數(shù)超過(guò)一半的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • mybatis?foreach?list特殊處理方式

    mybatis?foreach?list特殊處理方式

    這篇文章主要介紹了mybatis?foreach?list特殊處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評(píng)論