笔者写这篇博客是因为近期遇到的关于两者之间的分页代码差距,其实之前也遇见过但是没有去整理这篇博客,但由于还是被困扰了小一会儿时间,所以还是需要加深记忆。其实会看前后端传参解决这个问题很快、不麻烦。关于这两个框架的分页代码问题主要就是在业务层和MyBatis的SQL问题。注意:这里我不展示前端接口,需要知道的是前端会传给后端当前页(page)以及每页条数(size)。后端根据两个参数去实现分页(limit)。这里最容易踩坑的一个点:在MyBatis的分页中,(当前页需要 - 1) * size传入#{page},而在MyBatis-Plus中的new Page(page,size),则不需要去做运算,在Plus已经做好了这一点。详情请看下面代码块。
那么关于MyBatis这个半ORM框架来说,SQL还是需要自己去写在xml中的或者是在注解上实现。这里我就采用xml种实现的方式去实现啦。为了减少代码量(真实开发不能这样的哈),我就将所有业务代码都放到控制层中。
org.mybatis.spring.boot mybatis-spring-boot-starter2.2.2 mysql mysql-connector-java8.0.31 com.github.pagehelper pagehelper-spring-boot-starter1.2.12
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/table?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml # 对应xml的位置 type-aliases-package: com.chf.entity # 对应namespace的实体类包名 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志类型 map-underscore-to-camel-case: true # 字段与属性的驼峰规则 # MyBatis的分页插件 这是最重要的 pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: count=countsql
@RestController @RequestMapping("/emp") public class EmpController{ //@Autowired //private EmpService empService; // 为了博客简化代码量 所以直接调用Dao层接口 @Autowired private EmpMapper empMapper; /** * 分页查询 * @param pageNum 当前页数 * @param pageSize 每页条数 * @return */ @GetMapping("/findAll/{page}/{size}") public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){ Integer page = (pageNum - 1) * pageSize; ListempList = empMapper.selectByPage(page, pageSize); PageInfo pageInfo = new PageInfo<>(empList); pageInfo.setTotal(empMapper.selectCount()); return R.ok(pageInfo); } }
@Mapper public class EmpMapper{ ListselectByPage(@Param("page") Integer pageNum, @Param("size") Integer pageSize); Integer selectCount(); }
返回的分页信息是在data中的list
com.alibaba druid1.1.22 com.baomidou mybatis-plus-boot-starter3.4.3 mysql mysql-connector-java8.0.17
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/table?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 mybatis-plus: mapper-locations: classpath:mapper/*.xml # 对应xml的位置 type-aliases-package: com.chf.entity # 对应namespace的实体类包名 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志类型 map-underscore-to-camel-case: true # 字段与属性的驼峰规则
@RestController @RequestMapping("/emp") public class EmpController{ @Autowired private EmpService empService; /** * 分页查询 * @param pageNum 当前页数 * @param pageSize 每页条数 * @return */ @GetMapping("/findAll/{page}/{size}") public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){ Pagepage = new Page<>(pageNum, pageSize); // 此方法还可以传入第二个参数:QueryWrapper条件构造器 // 用于增添一些查询条件用的 这里就不做展示了 empService.page(page); // 如果是调用数据访问层的就是selectPage()方法即以下语句 // mapper.selectPage(page, QueryWrapper); return R.ok(page); } }
public interface EmpService extends IService{ }
@Service public class EmpServiceImpl extends ServiceImplimplements EmpService { }
@Mapper public interface EmpMapper extends BaseMapper{ }
可以看到在MyBatis-Plus返回前端的参数中使用records封装分页信息。看到这里以为结束了吗?仔细看total(总条数)会发现怎么会是0?还有pages(总页数)也是0,学过MyBatis-Plus应该都知道为了完成分页所以还需要配置分页插件才可以实现真正的分页。所以需要再添加一个配置类,代码如下:
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } }
添加好分页插件再次查询接口就会看到总条数和总页数都是真实的数值了。
其实MyBatis底层就是调用Github的PageHelper插件工具。但是如果直接使用Github的分页的话会更加简便,但是有一个坑。只需要将上面MyBatis的业务代码改为以下代码块。相比而言,代码行数一致。多了一句PageMethod.startPage()、少了一句设置总条数的语句。
@RestController @RequestMapping("/emp") public class EmpController{ //@Autowired //private EmpService empService; // 为了博客简化代码量 所以直接调用Dao层接口 @Autowired private EmpMapper empMapper; /** * 分页查询 * @param pageNum 当前页数 * @param pageSize 每页条数 * @return */ @GetMapping("/findAll/{page}/{size}") public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){ // 由于PageHelper继承PageMethod但未重写方法 所以写成下面的语句 Page
这里的踩坑处就是Page对象一定要在想要查询的列表前先初始出来,否则过滤无效。这样子Paga中的列表信息才是我们想要的查询出来的信息。比如说:业务中我需要查询A、B列表进行拼接啥的。主要是为了返回A列表的数据,所以只有以下两种组合方式(大于号表示业务代码中靠前出现):
Page > AList > BList。BList > Page > AList。
关于这个问题。我很好奇的追入源码中阅读。在PageMethod的startPage()中对Page对象进行设置。并且多了一步判断当前线程(ThreadLocal)是否存在Page对象。不存在则创建,存在则直接分页。至于源码追踪请参考这篇文章:Mybatis第三方PageHelper插件分页原理-腾讯云开发者社区-腾讯云
public abstract class PageMethod { protected static final ThreadLocalLOCAL_PAGE = new ThreadLocal(); public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) { Page page = new Page(pageNum, pageSize, count); page.setReasonable(reasonable); page.setPageSizeZero(pageSizeZero); // 判断当前线程中是否存在Page对象(执行排序Order by之后) Page oldPage = getLocalPage(); // (Page)LOCAL_PAGE.get() if (oldPage != null && oldPage.isOrderByOnly()) { page.setOrderBy(oldPage.getOrderBy()); } setLocalPage(page); // LOCAL_PAGE.set(page); return page; } }