欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

分頁設(shè)計

為什么需要分頁?

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

如何設(shè)計分頁查詢參數(shù)?

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

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

我們可以設(shè)計一個公共的父類PageQuery來幫助提供默認(rèn)的參數(shù),同時我們在開發(fā)中也會用到mybatisplus,提供出轉(zhuǎn)成page對象的方法

@Data
@ApiModel(description = "分頁請求參數(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 = "頁碼", example = "1")
    @Min(value = 1, message = "頁碼不能小于1")
    private Integer pageNo = DEFAULT_PAGE_NUM;

    @ApiModelProperty(value = "每頁大小", example = "5")
    @Min(value = 1, message = "每頁查詢數(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);
        // 是否手動指定排序方式
        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è)計優(yōu)點

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

使用方式

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

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

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

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

在實際開發(fā)中,我們常遇到這樣的問題:

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

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

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

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

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

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

@Data
public class CouponFormDTO {
    @NotNull(message = "優(yōu)惠券類型不能為空")
    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;
}

常用注解矩陣:

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

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

JSR 380 校驗流程

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

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

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

自定義枚舉校驗的黑科技

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

我們在設(shè)計數(shù)據(jù)庫時通常會使用某個數(shù)字來代表某個狀態(tài),如:

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

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

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

缺陷

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

自定義注解解決方案

定義枚舉校驗注解

@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 {};
}

實現(xià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生命周期

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

JSR 380規(guī)范實現(xiàn)要點

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

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

有時,僅僅靠我們的校驗比較復(fù)雜,這時我們可能需要自己來編寫校驗邏輯

我們可以通過自定義注解+AOP來幫我們實現(xiàn)

/**
 * 實現(xiàn)后在接口訪問時如果接口實現(xiàn)了這個接口
 * 會被自動自行接口check進(jìn)行校驗
 **/
public interface Checker<T> {

    /**
     * 用于實現(xiàn)validation不能校驗的數(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("目錄類型1:章,2:節(jié),3:測試")
    @NotNull(message = "")
    private Integer type;
    @ApiModelProperty("章節(jié)練習(xí)名稱")
    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() {
        //名稱為空校驗
        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);
        }
        //名稱長度問題
        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ù)校驗器

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

定義切面類

@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ù)是否實現(xiàn)了Checker接口
            for (Object arg : args){
                if(arg instanceof Checker) {
                    //調(diào)用check方法,校驗業(yè)務(wù)邏輯
                    ((Checker)arg).check();
                }else if(arg instanceof List){
                    //如果參數(shù)是一個集合也要校驗
                    CollUtils.check((List) arg);
                }
            }
        }
    }
}

工具方法

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

    /**
     * 集合校驗邏輯
     *
     * @param data 要校驗的集合
     * @param <T> 集合元素類型
     */
    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è)務(wù)限制的字段
    public CourseSaveVO save(@RequestBody @Validated(CourseSaveBaseGroup.class) CourseBaseInfoSaveDTO courseBaseInfoSaveDTO) {
        return courseDraftService.save(courseBaseInfoSaveDTO);
    }

注意:切面類沒有納入ioc容器管理,如果是單體項目加上component注解即可,如果是多模塊項目,使用自動裝配功能

總結(jié)

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

相關(guān)文章

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

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

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

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

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

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

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

    Java2 JDK安裝和配置教程

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

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

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

    java實現(xiàn)兩個對象之間傳值及簡單的封裝

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

    springboot中如何引入AOP切面編程

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

    Java多線程中的Phaser詳解

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

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

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

    mybatis?foreach?list特殊處理方式

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

最新評論