使用
分页是后端开发的一个基础问题 基本上所有新手都会遇到,从前比较原始的分页 可能需要我们自己写分页 比如拿到分页参数 页数和num,然后自己把它拼接到sql里面去。
MybatisPlus提供了一个分页插件可以很方便的使用 对业务人员比较友好
先给一个简单例子:
@Beanpublic 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 {@Autowiredprivate 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方法的实现类 全部遍历一遍 每次遍历 判断需不需要拦截 如果需要就拦截处理 依次类推 遍历到分页拦截器的时候 发现你的语句中有分页相关参数 就处理分页逻辑 加工sqlwhile(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、RowBounds 和 SQL 语句组成的。具体来说,CacheKey 的创建是在执行查询前由 Executor 对象调用 createCacheKey 方法完成的。它的作用是用来检查缓存中是否存在相同查询条件的结果,如果存在,则直接从缓存中获取结果,而不需要再次执行查询。在执行查询时,MyBatis会将当前的 CacheKey 与缓存中已有的键进行比对,如果存在相同的键,则可以直接从缓存中获取查询结果,而不需要再次执行数据库查询操作,从而提高查询效率。return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);}