使用
分页是后端开发的一个基础问题 基本上所有新手都会遇到,从前比较原始的分页 可能需要我们自己写分页 比如拿到分页参数 页数和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、RowBounds 和 SQL 语句组成的。
具体来说,CacheKey 的创建是在执行查询前由 Executor 对象调用 createCacheKey 方法完成的。它的作用是用来检查缓存中是否存在相同查询条件的结果,如果存在,则直接从缓存中获取结果,而不需要再次执行查询。
在执行查询时,MyBatis会将当前的 CacheKey 与缓存中已有的键进行比对,如果存在相同的键,则可以直接从缓存中获取查询结果,而不需要再次执行数据库查询操作,从而提高查询效率。
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}