好久没水后端的东西了,最近在做vue项目写前端的代码,所以cloud也停进度了,吃完饭突然记得我没有在博客里写分页的东西,虽然项目中用到了,但是没有拎出来,这里就拎出来看看。
导入最新的mp依赖是第一步不然太低的版本什么都做不了,3,1以下的好像连分页插件都没有加进去,所以我们用最新的3.5的,保证啥都有:
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>3.5.2version>
- dependency>
这里我们需要认识两个插件:mp的核心插件MybatisPlusInterceptor与自动分页插件PaginationInnerInterceptor。
MybatisPlusInterceptor的源码(去掉中间的处理代码):
- public class MybatisPlusInterceptor implements Interceptor {
- private List
interceptors = new ArrayList(); - public MybatisPlusInterceptor() {}
- public Object intercept(Invocation invocation) throws Throwable {}
- public Object plugin(Object target) {}
- public void addInnerInterceptor(InnerInterceptor innerInterceptor) {}
- public List
getInterceptors() {} - public void setProperties(Properties properties) {}
- public void setInterceptors(final List
interceptors) {} - }
我们可以发现它有一个私有的属性列表 List
InnerInterceptor源码:
- public interface InnerInterceptor {
- default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
- return true;
- }
-
- default void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
- }
-
- default boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
- return true;
- }
-
- default void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
- }
-
- default void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
- }
-
- default void beforeGetBoundSql(StatementHandler sh) {
- }
-
- default void setProperties(Properties properties) {
- }
- }
不难发现这个接口的内容大致就是设置默认的属性,从代码的意思上就是提供默认的数据库操作执行时期前后执行的一些逻辑,谁实现它的方法会得到新的功能?
再看看PaginationInnerInterceptor插件的源码:
- public class PaginationInnerInterceptor implements InnerInterceptor {
- protected static final List
COUNT_SELECT_ITEM = Collections.singletonList((new SelectExpressionItem((new Column()).withColumnName("COUNT(*)"))).withAlias(new Alias("total"))); - protected static final Map
countMsCache = new ConcurrentHashMap(); - protected final Log logger = LogFactory.getLog(this.getClass());
- protected boolean overflow;
- protected Long maxLimit;
- private DbType dbType;
- private IDialect dialect;
- protected boolean optimizeJoin = true;
-
- public PaginationInnerInterceptor(DbType dbType) {
- this.dbType = dbType;
- }
-
- public PaginationInnerInterceptor(IDialect dialect) {
- this.dialect = dialect;
- }
-
- public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
- IPage> page = (IPage)ParameterUtils.findPage(parameter).orElse((Object)null);
- if (page != null && page.getSize() >= 0L && page.searchCount()) {
- MappedStatement countMs = this.buildCountMappedStatement(ms, page.countId());
- BoundSql countSql;
- if (countMs != null) {
- countSql = countMs.getBoundSql(parameter);
- } else {
- countMs = this.buildAutoCountMappedStatement(ms);
- String countSqlStr = this.autoCountSql(page, boundSql.getSql());
- MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
- countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
- PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
- }
-
- CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
- List
- long total = 0L;
- if (CollectionUtils.isNotEmpty(result)) {
- Object o = result.get(0);
- if (o != null) {
- total = Long.parseLong(o.toString());
- }
- }
-
- page.setTotal(total);
- return this.continuePage(page);
- } else {
- return true;
- }
- }
-
- public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {...........省略之后全部的内容........}
我们不难发现它实现了来自于InnerInterceptor的方法,这里面的源码有时间需要好好处处逻辑。
我们知道了分页插件和核心插件的关系,也就是我们可以将分页插件添加入核心插件内部的插件链表中去,从而实现多功能插件的使用。
配置mp插件,并将插件交由spring管理(我们用的是springboot进行测试所以不需要使用xml文件):
- import com.baomidou.mybatisplus.annotation.DbType;
- import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
- import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- public class MpConfig {
-
- /*分页插件的配置*/
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor() {
- /*创建mp拦截器*/
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- /*创建分页插件*/
- PaginationInnerInterceptor pagInterceptor = new PaginationInnerInterceptor();
- /*设置请求的页面大于最大页容量后的请求操作,true回调第一页,false继续翻页,默认翻页*/
- pagInterceptor.setOverflow(false);
- /*设置单页分页的条数限制*/
- pagInterceptor.setMaxLimit(500L);
- /*设置数据库类型*/
- pagInterceptor.setDbType(DbType.MYSQL);
- /*将分页拦截器添加到mp拦截器中*/
- interceptor.addInnerInterceptor(pagInterceptor);
-
- return interceptor;
- }
- }
配置完之后写一个Mapper接口:
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.hlc.mp.entity.Product;
- import org.apache.ibatis.annotations.Mapper;
-
- @Mapper
- public interface ProductMapper extends BaseMapper
{ - }
为接口创建一个服务类(一定按照mp编码的风格来):
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.IService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.hlc.mp.entity.Product;
- import com.hlc.mp.mapper.ProductMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import java.util.List;
-
- @Service(value = "ProductService")
- public class ProductServiceImpl extends ServiceImpl
- implements IService
{ -
- @Autowired
- ProductMapper productMapper;
-
- /**
- * 根据传入的页码进行翻页
- *
- * @param current 当前页码(已经约定每页数据量是1条)
- * @return 分页对象
- */
- public Page
page(Long current) { - /*current首页位置,写1就是第一页,没有0页之说,size每页显示的数据量*/
- Page
productPage = new Page<>(current, 1); - /*条件查询分页*/
- QueryWrapper
queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("status", 0);
- productMapper.selectPage(productPage, queryWrapper);
- return productPage;
- }
- }
到这里我们可以看到分页的具体方法就是,先创建一个分页对象,规定页码和每一页的数据量的大小,其次确定查询操作的范围,并使用BaseMapper
测试类:
- @Test
- public void testPage(){
- IPage
productIPage = productService.page(2L); - productIPage.getRecords().forEach(System.out::println);
- System.out.println("当前页码"+productIPage.getCurrent());
- System.out.println("每页显示数量"+productIPage.getSize());
- System.out.println("总页数"+productIPage.getPages());
- System.out.println("数据总量"+productIPage.getTotal());
- }
运行查看分页结果:

我们可以发现都正常的按照我们传入的页码去查询对应的页数据了,因为我设置的每页只展示一条数据,所以ID如果对应页码就说明分页成功。