首页 > 其他分享 >MybatisPlus分页查询详解

MybatisPlus分页查询详解

时间:2024-08-18 14:24:27浏览次数:13  
标签:pageDTO MybatisPlus 分页 private page 详解 user return public

MybatisPlus分页查询详解

一直对于分页查询的插件用的不是很熟练,这次在学习mp的时候又一次学到了分页查询,在这个过程中发现学到的东西挺多的,想着可以分享给大家,往下看前请保证对泛型以及函数式接口编程有一定了解

1 MybatisPlus的基础介绍

这边主要是讲解他的一个分页功能因此基础的用法并不会很详细(可以自行学习)

1.1 配置环境

  • 引入相关依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    
  • 连接数据库

    spring:
      datasource:
        url: jdbc:mysql://10.0.0.232:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: 数据库登录账号
        password: 你数据库密码
    

1.2 核心功能

  • Mapper

    为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD

    请添加图片描述

    因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。

    public interface UserMapper extends BaseMapper<User> {
    }
    
  • 新建一个测试类,编写几个单元测试,测试基本的CRUD功能:

    @SpringBootTest
    class UserMapperTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void testInsert() {
            User user = new User();
            user.setId(5L);
            user.setUsername("Lucy");
            user.setPassword("123");
            user.setPhone("18688990011");
            user.setBalance(200);
            user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            userMapper.insert(user);
        }
    
        @Test
        void testSelectById() {
            User user = userMapper.selectById(5L);
            System.out.println("user = " + user);
        }
    
        @Test
        void testSelectByIds() {
            List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L));
            users.forEach(System.out::println);
        }
    
        @Test
        void testUpdateById() {
            User user = new User();
            user.setId(5L);
            user.setBalance(20000);
            userMapper.updateById(user);
        }
    
        @Test
        void testDelete() {
            userMapper.deleteById(5L);
        }
    }
    
  • 条件构造器Wrapper

    对于一些简单的CRUD操作MP都提供了现成的方法可以直接调用,但是稍微复杂些的带where的就需要用到条件构造器rapper

    请添加图片描述

    ​ 在我看来所谓的Wrapper其实就是将Sql语句中 where 后的的条件进行了封装以便于我们将进行简洁的代码开发,我们可以在上图看到继承结构于集合类似其实我们最终使用的还是最底层的实现类,由其名字就可知操作上分为查询和更新,其中又有lambda表达式的写法和正常构造的写法

    这里就给两个简单是示例便于理解

    • QueryWrapper

      
      @Test查询出名字中带o的,存款大于等于1000元的人
      void testQueryWrapper() {
          // 1.构建查询条件 where name like "%o%" AND balance >= 1000
          QueryWrapper<User> wrapper = new QueryWrapper<User>()
                  .select("id", "username", "info", "balance")
                  .like("username", "o")
                  .ge("balance", 1000);
          // 2.查询数据
          List<User> users = userMapper.selectList(wrapper);
          users.forEach(System.out::println);
      }
      
    • UpdateWrapper

      
      @Test更新id为1,2,4的用户的余额,扣200
      void testUpdateWrapper() {
          List<Long> ids = List.of(1L, 2L, 4L);
          // 1.生成SQL
          UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                  .setSql("balance = balance - 200") // SET balance = balance - 200
                  .in("id", ids); // WHERE id in (1, 2, 4)
              // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
          // 而是基于UpdateWrapper中的setSQL来更新
          userMapper.update(null, wrapper);
      }
      

      显然这种写法固定了字段名称,在编程中是不推荐 因此还是推荐使用基于Lambda的Wrapper

      @Test
      void testLambdaQueryWrapper() {
          // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
          QueryWrapper<User> wrapper = new QueryWrapper<>();
          wrapper.lambda()
                  .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                  .like(User::getUsername, "o")
                  .ge(User::getBalance, 1000);
          // 2.查询
          List<User> users = userMapper.selectList(wrapper);
          users.forEach(System.out::println);
      }
      

      不难看出本质上就是基于变量的gettter方法结合反射技术得到对应字段传递给MP.

      对于以上代码中的userMapper 都是继承自MP给我们提供的BaseMapper 里面封装了许多基本CRUD

  • Service接口

    ​ MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。 通用接口为IService,默认实现为ServiceImpl

    • 基本用法

      由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。

      首先,定义IUserService,继承IService

      public interface IUserService extends IService<User> {
          // 拓展自定义方法
      }
      

      然后,编写UserServiceImpl类,继承ServiceImpl,实现UserService

      @Service
      public class UserServiceImpl extends ServiceImpl<UserMapper, User>
                                                                                                              implements IUserService {
      }
      

      其中UserMapper为svc要调用的mapper,User为操作的实体类

      这里我们直接介绍最为常用的Lambda形式

      Service中对LambdaQueryWrapperLambdaUpdateWrapper的用法进一步做了简化我们无需自己通过new的方式来创建Wrapper,而是直接调用lambdaQuerylambdaUpdate方法:可以构造一个wrapper,还需要在链式编程的最后添加一个想要得到的结果

      ===============================lambdaQuery
      @GetMapping("/list")
      @ApiOperation("根据id集合查询用户")
      public List<UserVO> queryUsers(UserQuery query){
          // 1.组织条件
          String username = query.getName();
          Integer status = query.getStatus();
          Integer minBalance = query.getMinBalance();
          Integer maxBalance = query.getMaxBalance();
          // 2.查询用户
          List<User> users = userService.lambdaQuery()
                  .like(username != null, User::getUsername, username)
                  .eq(status != null, User::getStatus, status)
                  .ge(minBalance != null, User::getBalance, minBalance)
                  .le(maxBalance != null, User::getBalance, maxBalance)
                  .list();//这里就是将结果转换为list集合
          
          // 3.处理vo
          return BeanUtil.copyToList(users, UserVO.class);
      }
      ===============================lambdaUpdate===============================
      @Override
      @Transactional
      public void deductBalance(Long id, Integer money) {
          // 1.查询用户
          User user = getById(id);
          // 2.校验用户状态
          if (user == null || user.getStatus() == 2) {
              throw new RuntimeException("用户状态异常!");
          }
          // 3.校验余额是否充足
          if (user.getBalance() < money) {
              throw new RuntimeException("用户余额不足!");
          }
          // 4.扣减余额 update tb_user set balance = balance - ?
          int remainBalance = user.getBalance() - money;
          lambdaUpdate()
                  .set(User::getBalance, remainBalance) // 更新余额
                  .set(remainBalance == 0, User::getStatus, 2) // 动态判断,是否更新status
                  .eq(User::getId, id)
                  .eq(User::getBalance, user.getBalance()) // 乐观锁
                  .update();
      }
      

      ok 通过以上的介绍相信大家已经可以正式的开始学习分页插件的用法啦!

2 插件功能

MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:

  • PaginationInnerInterceptor:自动分页
  • TenantLineInnerInterceptor:多租户
  • DynamicTableNameInnerInterceptor:动态表名
  • OptimisticLockerInnerInterceptor:乐观锁
  • IllegalSQLInnerInterceptor:sql 性能规范
  • BlockAttackInnerInterceptor:防止全表更新与删除

这里我们通过学习分页插件来学习插件的用法

2.1 分页插件

在我们没有引入插件的情况下,默认是不支持分页功能的IServiceBaseMapper中的分页方法都无法正常起效。 所以,我们必须配置分页插件。

  • 配置分页插件

    这里我们创建一个配置类 并且添加分页插件

    @Configuration
    public class MybatisConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            // 初始化核心插件
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 添加分页插件
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
    

    其实可以看到本质上就是创建了一个对sql语句的拦截器

  • 基本使用

    @Test
    void testPageQuery() {
        // 1.分页查询,new Page()的两个参数分别是:页码、每页大小
        Page<User> p = userService.page(new Page<>(2, 2));
        // 2.总条数
        System.out.println("total = " + p.getTotal());
        // 3.总页数
        System.out.println("pages = " + p.getPages());
        // 4.数据
        List<User> records = p.getRecords();
        records.forEach(System.out::println);
    }
    
  • 接下来我们创建一个通用的分页实体

    现在要实现一个分页查询的接口,接口规范如下:

    参数说明
    请求方式GET
    请求路径/users/page
    特殊说明如果排序字段为空,默认按照更新时间排序排序字段不为空,则按照排序字段排序
    //请求参数
    {
        "pageNo": 1,
        "pageSize": 5,
        "sortBy": "balance",
        "isAsc": false,
        "name": "o",
        "status": 1
    }
    //返回参数
    {
        "total": 100006,
        "pages": 50003,
        "list": [
            {
                "id": 1685100878975279298,
                "username": "user_9****",
                "info": {
                    "age": 24,
                    "intro": "英文老师",
                    "gender": "female"
                },
                "status": "正常",
                "balance": 2000
            }
        ]
    }
    

    通用的开发规范中,在这里我们需要定义三个实体类用于接受数据实体,返回数据实体以及一个分页结果实体

    • UserQuery:分页查询条件的实体,包含分页,排序参数,过滤条件
    • PageDTO:分页结果实体,包含总条数、总页数、当前页数据
    • UserVO:用户页面视图实体
  • 创建实体

    由于分页查询这个功能不仅仅在某一张表上需要使用,因此我们可以将分页,排序参数单独定义一个通用类,其他需要查询的表实体进行继承即可这里我们首先创建一个PageQuery类包含参数

    下方的@ApiModel @ApiModelProperty 注解是为了swagger接口测试时易读

    @Data
    @ApiModel(description = "分页查询实体")
    public class PageQuery {
        @ApiModelProperty("页码")
        private Long pageNo;
        @ApiModelProperty("页码")
        private Long pageSize;
        @ApiModelProperty("排序字段")
        private String sortBy;
        @ApiModelProperty("是否升序")
        private Boolean isAsc;
    }
    

    这里我们针对用户表进行分页,因此使用用户表继承

    @Data
    @EqualsAndHashCode
    @ApiModel(description = "用户查询条件实体")
    public class UserQuery extends PageQuery{
        @ApiModelProperty("用户名关键字")
        private String name;
        @ApiModelProperty("用户状态:1-正常,2-冻结")
        private Integer status;
    }
    

    接下来我们创建UserVO用户页面视图实体

    @Data
    @ApiModel(description = "用户VO实体")
    public class UserVO {
    
        @ApiModelProperty("用户id")
        private Long id;
    
        @ApiModelProperty("用户名")
        private String username;
    
        @ApiModelProperty("详细信息")
        private String info;
    
        @ApiModelProperty("使用状态(1正常 2冻结)")
        private Integer status;
    
        @ApiModelProperty("账户余额")
        private Integer balance;
    
        @ApiModelProperty("用户地址")
        private List<AddressVO> addresses;
    }
    
    

    最后我们创建分页结果实体

    @Data
    public class PageDTO<UserVO> {
        @ApiModelProperty("总条数")
        private Long total;
        @ApiModelProperty("总页数")
        private Long pages;
        @ApiModelProperty("集合")
        private List<UserVO> list;
    }
    

    实体创建完成 ,接下来我们正式开发接口

  • 开发接口

    controller层

    @RestController
    @RequestMapping("users")
    @RequiredArgsConstructor
    public class UserController {
    
        private final UserService userService;
    
        @GetMapping("/page")
        public PageDTO<UserVO> queryUsersPage(UserQuery query){
            return userService.queryUsersPage(query);
        }
    
    }
    

    svc 以及svcImpl

    PageDTO<UserVO> queryUsersPage(PageQuery query);
    
    @Override
    public PageDTO<UserVO> queryUsersPage(PageQuery query) {
        // 1.构建条件
        	// 过滤条件
         String name = query.getName();
         Integer status = query.getStatus();
            
        // 1.1.分页条件
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
        // 1.2.排序条件
        if (query.getSortBy() != null) {
            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
        }else{
            // 默认按照更新时间排序
            page.addOrder(new OrderItem("update_time", false));
        }
        // 2.查询
            Page<User> userPage = lambdaQuery()
                    .like(name != null, User::getUsername, name)
                    .eq(status != null, User::getStatus, status)
                    .page(page);
        // 3.数据非空校验
        List<User> records = userpage.getRecords();
        if (records == null || records.size() <= 0) {
            // 无数据,返回空结果
            return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());
        }
        // 4.有数据,转换
        List<UserVO> list = BeanUtil.copyToList(records, UserVO.class);
        // 5.封装返回
        return new PageDTO<UserVO>(page.getTotal(), page.getPages(), list);
    }
    

    总的步骤可以分为:

    1. 构建相关条件
    2. 通过分页条件构造分页
    3. 判断排序条件是否存在
    4. 查询
    5. 数据的非空校验
    6. 数据的转换
    7. 返回

    这里我们可以看到整个过程其实是相对比较繁琐的,我们可以注意到其实就是从请求参数query–>pageDTO转换的前3个步骤以及从page–>pageDTO转换的后3个步骤都是相对固定的,因此我们可以将其抽象到相关的实体类中简化代码的编写过程!

    • 对于从query–>page

      我们可以改写PageQuery

      @Data
      public class PageQuery {
          private Integer pageNo = 1;
          private Integer pageSize = 5;
          private String sortBy;
          private Boolean isAsc = true;
      
          public <T> Page<T> toMpPage(OrderItem ... orderItems) {
              //1 构建分页条件
              Page<T> page = Page.of(pageNo, pageSize);
              // 2 排序条件
              // 2.1 判断前端是否传入排序字段
              if(sortBy != null) {
                  page.addOrder(new OrderItem(sortBy, isAsc));
                  return page;
              }
              //2.2 检查是否手动传入排序字段
              if( orderItems != null && orderItems.length > 0 ) {
                  page.addOrder(orderItems);
                  return page;
              }
              return page;
          }
          //直接指定
          public <T>Page<T> toMpPage(String orderItem,boolean isAsc) {
              return this.toMpPage(orderItem,isAsc);
          }
          public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
              return toMpPage("create_time", false);
          }
          public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
              return toMpPage("update_time", false);
          }
      
      }
      

      可以注意到这里我们定义了一个泛型T,这是因为我们其实并不知道使用分页查询时每一页的数据类型是啥,而使用泛型T就可以通用化

      这样我们的quer–>page 就可以简化成

              Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
      
    • 对于page–>pageDTO

      我们改造pageDTO

      @Data
      public class PageDTO<T> {
          @ApiModelProperty("总条数")
          private Long total;
          @ApiModelProperty("总页数")
          private Long pages;
          @ApiModelProperty("集合")
          private List<T> list;
      
          /**
           * 对po到vo的转换
           * @param userPage
           * @param clazz
           * @return
           * @param <PO>
           * @param <VO>
           */
          public static <PO,VO> PageDTO<VO> of(Page<PO> userPage , Class<VO> clazz){
              // 封装VO结果
              PageDTO<VO> pageDTO = new PageDTO<>();
              pageDTO.setTotal(userPage.getTotal());
              pageDTO.setPages(userPage.getPages());
      
              List<PO> list = userPage.getRecords();
              //判断数据为空
              if(CollUtil.isNotEmpty(list)){
                  pageDTO.setList(Collections.emptyList());
                  return pageDTO;
              }
              //PO转VO
              List<VO> userVOList = BeanUtil.copyToList(list, clazz);
              pageDTO.setList(userVOList);
              //4 返回
              return pageDTO;
          }
      }
      
      

      这里同样定义泛型 这个转换其实本质上就是一个PO–>VO的转换因此我们为了通用化可以定义<PO,VO>两个泛型

      但是这样就要求PO和VO中要转换的字段名一致,不可以进行特殊的转换,我们可以重载函数并且利用函数式接口编程定义一个转换器进行特定转换

          /**
           * 利用函数式接口编程创建一个转换器可以特定转换
           * @param userPage
           * @param convert
           * @return
           * @param <PO>
           * @param <VO>
           */
          public static <PO,VO> PageDTO<VO> of(Page<PO> userPage , Function<PO,VO> convert){
              //3 封装VO结果
              PageDTO<VO> pageDTO = new PageDTO<>();
              pageDTO.setTotal(userPage.getTotal());
              pageDTO.setPages(userPage.getPages());
      
              List<PO> list = userPage.getRecords();
              //判断数据为空
              if(CollUtil.isNotEmpty(list)){
                  pageDTO.setList(Collections.emptyList());
                  return pageDTO;
              }
              //PO转VO 
              List<VO> userVOList = list.stream().map(convert).collect(Collectors.toList());
              pageDTO.setList(userVOList);
              //4 返回
              return pageDTO;
          }
      

      这里利用到了stream流使用map转换,这样我们就可以在调用处编写特定的转换逻辑

      最终实体为

      @Data
      public class PageDTO<T> {
          @ApiModelProperty("总条数")
          private Long total;
          @ApiModelProperty("总页数")
          private Long pages;
          @ApiModelProperty("集合")
          private List<T> list;
      
          /**
           * 对po到vo的转换
           * @param userPage
           * @param clazz
           * @return
           * @param <PO>
           * @param <VO>
           */
          public static <PO,VO> PageDTO<VO> of(Page<PO> userPage , Class<VO> clazz){
              //3 封装VO结果
              PageDTO<VO> pageDTO = new PageDTO<>();
              pageDTO.setTotal(userPage.getTotal());
              pageDTO.setPages(userPage.getPages());
      
              List<PO> list = userPage.getRecords();
              //判断数据为空
              if(CollUtil.isNotEmpty(list)){
                  pageDTO.setList(Collections.emptyList());
                  return pageDTO;
              }
              //PO转VO
              List<VO> userVOList = BeanUtil.copyToList(list, clazz);
              pageDTO.setList(userVOList);
              //4 返回
              return pageDTO;
          }
      
          /**
           * 利用函数式接口编程创建一个转换器可以特定转换
           * @param userPage
           * @param convert
           * @return
           * @param <PO>
           * @param <VO>
           */
          public static <PO,VO> PageDTO<VO> of(Page<PO> userPage , Function<PO,VO> convert){
              //3 封装VO结果
              PageDTO<VO> pageDTO = new PageDTO<>();
              pageDTO.setTotal(userPage.getTotal());
              pageDTO.setPages(userPage.getPages());
      
              List<PO> list = userPage.getRecords();
              //判断数据为空
              if(CollUtil.isNotEmpty(list)){
                  pageDTO.setList(Collections.emptyList());
                  return pageDTO;
              }
              //PO转VO
              List<VO> userVOList = list.stream().map(convert).collect(Collectors.toList());
              pageDTO.setList(userVOList);
              //4 返回
              return pageDTO;
          }
      }
      

      最终我们的代码可以简化为如下

          @Override
          public PageDTO<UserVO> queryUsersByPage(UserQuery query) {
      //        过滤条件
              String name = query.getName();
              Integer status = query.getStatus();
      //        分页条件
              Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
      
      //        分页查询`
              Page<User> userPage = lambdaQuery()
                      .like(name != null, User::getUsername, name)
                      .eq(status != null, User::getStatus, status)
                      .page(page);
      
      //返回
      //        return PageDTO.of(userPage, UserVO.class); 无需进行特定的处理
              return PageDTO.of(userPage,user -> {
                  UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
                  //可以进行特定处理如(将后2个名字隐藏)
                  userVO.setUsername(userVO.getUsername().substring(0,userVO.getUsername().length() - 2) + "**");
                  return userVO;
              });
          }
      

3 总结

可以看到使用MP其实更多的式将我们在mapper层操作的过程隐藏起来了,便于我们更好的进行开发,我认为本篇最有价值的还是这里的通用化思想以及函数式接口编程的运用

标签:pageDTO,MybatisPlus,分页,private,page,详解,user,return,public
From: https://blog.csdn.net/plfcccc/article/details/141298678

相关文章

  • 二分查找算法详解及Python实现
    目录引言二分查找算法步骤二分查找的Python实现性能分析注意事项引言二分查找算法(BinarySearch)是一种在有序数组中查找某一特定元素的搜索算法。它的基本思想是:通过比较数组中间的元素与目标值的大小,将搜索区间缩小为一半,直到找到目标值或搜索区间被缩小为0。二分查......
  • 三剑客详解
    一、grep基本使用语法结构:模糊过滤查找内容grep'查找的内容'filecatfile|grep'查找屏幕上输出的内容'参考选项:r:递归过滤文件的内容v:取反w:过滤单词,以空格分割,精确匹配i:不区分大小写n:过滤到内容的具体行号c:统计单词次数o:查看匹配过程E:支持扩展正则A:显示查找内容......
  • 指针详解(二)
    目录1. const修饰指针1)const修饰变量2)const修饰指针变量2. 指针运算1)指针+- 整数2)指针-指针3)指针的关系运算3. 野指针1)野指针成因2)规避野指针4.assert断言5. 指针的使用和传址调用1)strlen的模拟实现2)传值调用和传址调用1. const修饰指针1)const修饰......
  • Redis 数据类型详解
    Redis是一个开源的内存数据结构存储系统,广泛应用于缓存、消息队列、实时数据分析等场景。Redis提供了多种数据类型,本文将详细介绍Redis的五种主要数据类型及其应用场景,并从概述、基本操作、应用场景和数据结构等方面进行深入探讨。1.字符串(String)概述字符串是Redis......
  • MySQL 数据类型详解
    MySQL是一种广泛使用的关系型数据库管理系统,它支持多种数据类型以满足各种应用场景的需求。本文将详细介绍MySQL支持的数据类型、它们的使用场景以及实现原理,并通过图示帮助读者更直观地理解。目录简介数值类型整型浮点型定点型日期和时间类型字符串类型字符串二进制字......
  • 【Java学习】Stream流详解
     所属专栏:Java学习Stream流是JDK 8引入的一个概念,它提供了一种高效且表达力强的方式来处理数据集合(如List、Set等)或数组。StreamAPI可以以声明性方式(指定做什么)来处理数据序列。流操作可以被分为两大类:中间操作(IntermediateOperations)和终端操作(TerminalOperation......
  • CDN 详解
    概述CDN(ContentDeliveryNetwork,内容分发网络)指基于部署在各地的机房服务,通过中心平台的负载均衡、内容分发、调度的能力,使用户就近获取所需内容,降低网络延迟,提升用户访问的响应速度和体验度CDN的关键技术CDN的关键技术包括内容发布、内容路由、内容交换和性能管理:内容发......
  • HTTPS 详解
    HTTPS是以安全为目标的HTTP通道,它在HTTP中加入SSL层以提高数据传输的安全性。HTTP被用于在Web浏览器和网站服务器之间传递信息,但以明文形式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务端之间的传输报文,就可以直接读懂其中的信息,因此HTTP......
  • 二分查找(算法详解+模板+例题)
    一.二分的定义二分法(Bisectionmethod)即一分为二的方法.设[a,b]为R的闭区间.逐次二分法就是造出如下的区间序列([an,bn]):a0=a,b0=b,且对任一自然数n,[an+1,bn+1]或者等于[an,cn],或者等于[cn,bn],其中cn表示[an,bn]的中点。二.基本思路1.将数组排序。2.一直将数组除以二,直到找到那......
  • VS2022实用调试技巧超详解
    文章目录1.什么是bug2.什么是调试(debug)3.Debug和Release4.VS调试快捷键4.1环境准备4.2调试快捷键5.监视和内存观察5.1监视5.2内存6.调试举例17.调试举例29.编程常见错误归类9.1编译型错误9.2链接型错误9.3运行时错误本文章以VS2022为例讲解调......