• 深入浅出mybatis分页


    MyBatis 分页插件 PageHelper

    PageHelper

    如何使用?

    如何使用分页插件

    1:添加依赖

            <dependency>
                <groupId>com.github.pagehelpergroupId>
                <artifactId>pagehelper-spring-boot-starterartifactId>
                <version>1.4.5version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    版本根据需要自行选择
    https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter

    第一步写一个没有分页的接口。

        @GetMapping("noPage")
        public java.util.List<Doc> list() {
            return mapper.selectList(null);
        }
    
    • 1
    • 2
    • 3
    • 4

    这个我相信都可以写的出来。

    第二步使用PageInfo包裹的接口

        @GetMapping("pageInfo")
        public PageInfo<Doc> pageInfo(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            return new PageInfo<>(mapper.selectList(null));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    返回值省略list

    {
      "total": 20,
      "list": [
      ],
      "pageNum": 2,
      "pageSize": 3,
      "size": 3,
      "startRow": 4,
      "endRow": 6,
      "pages": 7,
      "prePage": 1,
      "nextPage": 3,
      "isFirstPage": false,
      "isLastPage": false,
      "hasPreviousPage": true,
      "hasNextPage": true,
      "navigatePages": 8,
      "navigatepageNums": [
        1,
        2,
        3,
        4,
        5,
        6,
        7
      ],
      "navigateFirstPage": 1,
      "navigateLastPage": 7
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    第三步使用Page包裹返回接口

        @GetMapping("page")
        public Page<Doc> page(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            Page<Doc> page = (Page<Doc>) mapper.selectList(null);
            logger.info(page.toString());
            return page;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试发现page就是一个分页后的list,不带其他参数。

    Page{count=true, pageNum=3, pageSize=4, startRow=8, endRow=12, total=20, pages=5, reasonable=false, 
    pageSizeZero=false}
    
    • 1
    • 2

    第四步将第三步的page对象放入构造函数。

        @GetMapping("pageToPageInfo")
        public PageInfo<Doc> pageToPageInfo(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            Page<Doc> page = (Page<Doc>) mapper.selectList(null);
            return new PageInfo<>(page);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第五步自定义PageResult作为返回值

    @Data
    public class PageResult<T> {
    
        private static final Logger logger = LoggerFactory.getLogger(PageResult.class);
    
        /**
         * 第几页
         */
        private int pageNum;
        /**
         * 页面大小
         */
        private int pageSize;
        /**
         * 总页数
         */
        private int pages;
        /**
         * 总数
         */
        private long total;
        /**
         * 数据
         */
        private List<T> list;
    
        /**
         * 构造函数
         *
         * @param list
         */
        public PageResult(List<T> list) {
            if (list instanceof Page) {
                Page<T> page = (Page<T>) list;
                this.pageNum = page.getPageNum();
                this.pageSize = page.getPageSize();
                this.pages = page.getPages();
                this.total = page.getTotal();
            }
            this.list = list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
        @GetMapping("pageToPageResult")
        public PageResult<Doc> pageToPageResult(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            return new PageResult<>(mapper.selectList(null));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    {
      "pageNum": 2,
      "pageSize": 3,
      "pages": 7,
      "total": 20,
      "list": [
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    借鉴了这段实现逻辑直接从Page中拿到所需参数。

    在这里插入图片描述
    之所以我们自定义的PageResult可以成功是因为 list instanceof Page 这一行代码,mapper.selectList 经过处理后返回的不是普通的List而是Page对象。

    复杂分页

    对象转换

    一般来说我们拿到的DO和返回前端的VO不是同一个对象会进行相关处理,比如增删字段,这个时候如何正确分页呢?

    先演示一个错误案例

        @GetMapping("changeV1")
        public PageInfo<DocQueryRespVO> changeV1(Integer pageNum, Integer pageSize) {
            PageHelper.startPage(pageNum, pageSize);
            List<Doc> sourceList = mapper.selectList(null);
            List<DocQueryRespVO> targetList = sourceList.stream().map((source) -> {
                DocQueryRespVO target = new DocQueryRespVO();
                BeanUtils.copyProperties(source, target);
                Integer docType = target.getDocType();
                String msg = DocTypeEnum.getMsgByCode(docType);
                target.setDocTypeName(msg);
                return target;
            }).collect(Collectors.toList());
            return new PageInfo<>(targetList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    对原list每个对象修改字段值

    {
      "total": 5,
      "list": [
      ],
      "pageNum": 1,
      "pageSize": 5,
      "size": 5,
      "startRow": 0,
      "endRow": 4,
      "pages": 1,
      "prePage": 0,
      "nextPage": 0,
      "isFirstPage": true,
      "isLastPage": true,
      "hasPreviousPage": false,
      "hasNextPage": false,
      "navigatePages": 8,
      "navigatepageNums": [
        1
      ],
      "navigateFirstPage": 1,
      "navigateLastPage": 1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    可以看到这个total是错误的,应该是二十才对。分析一下为什么返回5。

        public PageInfo(List<? extends T> list) {
            this(list, DEFAULT_NAVIGATE_PAGES);
        }
    
    • 1
    • 2
    • 3
        public PageInfo(List<? extends T> list, int navigatePages) {
            super(list);
       	}
    
    • 1
    • 2
    • 3
        public PageSerializable(List<? extends T> list) {
            this.list = (List<T>) list;
            if(list instanceof Page){
                this.total = ((Page<?>)list).getTotal();
            } else {
                this.total = list.size();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    依次点进去发现,由于instanceof 失败所以取了list的total导致失效。可以加入代码验证 instanceof 输出。

    System.out.println(targetList instanceof Page);
    
    • 1
    方法一
        @GetMapping("changeV2")
        public PageInfo changeV2(Integer pageNum, Integer pageSize) {
    		//省略部分代码
            PageInfo pageInfo = new PageInfo(sourceList);
            pageInfo.setList(targetList);
            return pageInfo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    核心思想,利用泛型直接替换list保留原list中的分页信息。

    方法二
        @GetMapping("changeV3")
        public PageInfo<DocQueryRespVO> changeV3(Integer pageNum, Integer pageSize) {     
         	//省略部分代码  
            return PageHelperUtil.pageInfoCopy(sourceList, targetList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    封装一个工具类

        public static <source, target> PageInfo<target> pageInfoCopy(List<source> sList, List<target> tList) {
            if (org.springframework.util.CollectionUtils.isEmpty(sList) ||
                    org.springframework.util.CollectionUtils.isEmpty(tList)) {
                return null;
            }
            PageInfo<source> sourcePage = new PageInfo<>(sList);
            PageInfo<target> targetPage = new PageInfo<>(tList);
            org.springframework.beans.BeanUtils.copyProperties(sourcePage, targetPage);
            targetPage.setList(tList);
            return targetPage;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    核心思想利用BeanUtils.copyProperties将分页信息拷贝过来,仅仅替换list数据。

    手动分页

        public static <T> PageInfo<T> pageManual(List<T> list, int pageNum, int pageSize) {
            PageInfo<T> pageInfo = new PageInfo<>();
            //计算总页数
            int total = list.size();
            //如果总数为0直接返回
            if (total == 0) {
                return pageInfo;
            }
            // 计算最大页数
            int pageNumTotal;
            if (total <= pageSize) {
                pageNumTotal = 1;
            } else {
                pageNumTotal = total % pageSize == 0 ? (total / pageSize) : (total / pageSize) + 1;
            }
            //超出最大页码返回最后一页
            if (pageNum > pageNumTotal) {
                pageNum = pageNumTotal;
            }
            //如果页码 <= 0 返回第一页
            if (pageNum <= 0) {
                pageNum = 1;
            }
            //开始分页
            int st;
            int et;
            // 1 <= pageNum <= pageNumTotal
            if (pageNum < pageNumTotal) {
                st = (pageNum - 1) * pageSize;
                et = st + pageSize;
            } else {
                st = (pageNum - 1) * pageSize;
                et = total;
            }
            //subList包含st-et-1
            List<T> result = list.subList(st, et);
            pageInfo.setPageNum(pageNum);
            pageInfo.setPageSize(pageSize);
            pageInfo.setList(result);
            pageInfo.setTotal(total);
            pageInfo.setPages(pageNumTotal);
            pageInfo.setIsLastPage(pageNumTotal == pageSize);
            return pageInfo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    mybatisplus

    分页插件

    BaseMapper

    主要分页方法

        <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        
        <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    • 1
    • 2
    • 3

    如何使用

    第一步注册插件

        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第二步写一个简单的接口

        @GetMapping("selectPage")
        public Page<Doc> selectPage(Integer pageNum, Integer pageSize) {
            return mapper.selectPage(new Page<>(pageNum, pageSize), null);
        }
    
    • 1
    • 2
    • 3
    • 4

    分页结果

    {
      "records": [
        {
        },
        {
        },
        {
        },
        {
        },
        {
        }
      ],
      "total": 35,
      "size": 5,
      "current": 1,
      "orders": [],
      "optimizeCountSql": true,
      "searchCount": true,
      "countId": null,
      "maxLimit": null,
      "pages": 7
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    原理

    MybatisAutoConfiguration 中关键代码如下

      public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
          ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
          ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
          ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
          ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
    	
        this.interceptors = interceptorsProvider.getIfAvailable();
    
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当我们注册 MybatisPlusInterceptor 这个 bean后,interceptorsProvider.getIfAvailable()可以拿到Interceptor。

    复杂分页

    对象转换

            Page page = mapper.selectPage(new Page<>(pageNum, pageSize), null);
            List<Doc> records = page.getRecords();
            return page.setRecords(targetList);
    
    • 1
    • 2
    • 3

    和上面的差不多,都是取消泛型的编译检查来完成分页。

  • 相关阅读:
    微服务拆分
    聊一聊Redis事务
    01_ARM架构内存映射简介
    Spring面试题(2022)
    【毕业设计】31-基于单片机的农业蔬菜大棚温度自动控制系统设计(原理图工程+源码工程+仿真工程+答辩论文+答辩PPT)
    UG\NX二次开发 取消抑制特征 UF_MODL_unsuppress_feature
    Kafka Connect的内部结构和故障处理
    申请HTTPS证书
    Oracle常用对象精解(2)
    骨传导运动耳机哪个牌子好?值得买的骨传导运动耳机
  • 原文地址:https://blog.csdn.net/qq_37151886/article/details/127921669