MybatisPlus分页插件的使用和原理

使用

分页是后端开发的一个基础问题 基本上所有新手都会遇到,从前比较原始的分页 可能需要我们自己写分页 比如拿到分页参数 页数和num,然后自己把它拼接到sql里面去。

MybatisPlus提供了一个分页插件可以很方便的使用 对业务人员比较友好

先给一个简单例子:

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }Spring Boot应用的配置类中,通过@Bean注解配置MyBatis-Plus的分页插件


假设有一个User实体类:
public class User {
    private Long id;
    private String username;
    private Integer age;
    // 省略getter和setter方法
}

创建mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 自定义查询方法,例如根据用户名模糊查询
    List<User> selectByUsername(String username);
}

实现
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> getUserListWithPagination(int pageNum, int pageSize) {
        // 创建分页对象
        Page<User> page = new Page<>(pageNum, pageSize);
        // 执行分页查询,会自动进行分页
        return userMapper.selectPage(page, null).getRecords();
    }
}


Page对象表示分页信息,通过调用selectPage()方法执行分页查询,然后通过getRecords()方法获取查询结果。
当我们从前端拿到 pageNum, pageSize 参数的时候 直接用他们创建一个page 对象

然后MybatisPlus拦截器在执行sql的时候 会把带page对象的sql 拦截 加工成分页语句

原理

刚才说了 MybatisPlus拦截器在执行sql的时候 会把带page对象的sql 拦截 加工成分页语句

首先Mybatis会把参数存进 threadlocal 方便同一个线程中 上下文 参数的存取

然后使用拦截器 拦截sql 我们看源码:

  public MybatisPlusInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        Object[] args = invocation.getArgs();
        if (target instanceof Executor) {
            Executor executor = (Executor)target;
            Object parameter = args[1];
            boolean isUpdate = args.length == 2;
            MappedStatement ms = (MappedStatement)args[0];
            if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {
                RowBounds rowBounds = (RowBounds)args[2];
                ResultHandler resultHandler = (ResultHandler)args[3];
                BoundSql boundSql;
                if (args.length == 4) {
                    boundSql = ms.getBoundSql(parameter);
                } else {
                    boundSql = (BoundSql)args[5];
                }

                Iterator var11 = this.interceptors.iterator();

                while(var11.hasNext()) {
                    InnerInterceptor query = (InnerInterceptor)var11.next();
                    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
                        return Collections.emptyList();
                    }

                    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                }

                CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }

            if (isUpdate) {
                Iterator var8 = this.interceptors.iterator();

                while(var8.hasNext()) {
                    InnerInterceptor update = (InnerInterceptor)var8.next();
                    if (!update.willDoUpdate(executor, ms, parameter)) {
                        return -1;
                    }

                    update.beforeUpdate(executor, ms, parameter);
                }
            }
        } else {
            StatementHandler sh = (StatementHandler)target;
            Connection connections = (Connection)args[0];
            Integer transactionTimeout = (Integer)args[1];
            Iterator var16 = this.interceptors.iterator();

            while(var16.hasNext()) {
                InnerInterceptor innerInterceptor = (InnerInterceptor)var16.next();
                innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
            }
        }

        return invocation.proceed();
    }

看上面的源码

     if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) 
这里判断你的sql 是不是select 语句 
如果是

那么下面会把sql提取出来

                BoundSql boundSql;
                if (args.length == 4) {
                    boundSql = ms.getBoundSql(parameter);
                } else {
                    boundSql = (BoundSql)args[5];
                }


然后再往下 关键的一步就是 

   Iterator var11 = this.interceptors.iterator();
  
   这里创建nnerInterceptor接口的遍历 InnerInterceptor 的beforeQuery方法 有很多个实现 也就是说有很多个拦截器 这些拦截器 各司其事 有的拦截处理分页 有的拦截处理动态sql 

下面就把这些beforeQuery方法的实现类 全部遍历一遍 每次遍历 判断需不需要拦截 如果需要就拦截处理 

依次类推 遍历到分页拦截器的时候 发现你的语句中有分页相关参数 就处理分页逻辑 加工sql

                while(var11.hasNext()) {
                    InnerInterceptor query = (InnerInterceptor)var11.next();
                    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
                        return Collections.emptyList();
                    }

                    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                }

                CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);MyBatis 中,CacheKey 用于标识查询结果的缓存键。它是由当前查询的 MappedStatement、查询参数 parameter、RowBoundsSQL 语句组成的。

具体来说,CacheKey 的创建是在执行查询前由 Executor 对象调用 createCacheKey 方法完成的。它的作用是用来检查缓存中是否存在相同查询条件的结果,如果存在,则直接从缓存中获取结果,而不需要再次执行查询。

在执行查询时,MyBatis会将当前的 CacheKey 与缓存中已有的键进行比对,如果存在相同的键,则可以直接从缓存中获取查询结果,而不需要再次执行数据库查询操作,从而提高查询效率。
                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值