Mybatis 分页查询
- 1. 直接在 sql 中使用 limit
- 2. 使用 RowBounds
- 3. 使用 Mybatis 提供的拦截器机制
- 3.1 创建一个自定义拦截器类实现 Interceptor
- 3.2 创建分页查询函数 与 sql
- 3.3 编写拦截逻辑
- 3.4 注册 PageInterceptor 到 Mybatis 拦截器链中
- 3.5 测试
准备一个分页查询类
@Data
public class Page {// 开始下标 由当前页和每页大小计数而来// 这里也可以是当前页,在sql中进行开始下标的计算private int size;private int currentPage;
}
1. 直接在 sql 中使用 limit
创建一个分页查询函数
List<User> selectUserPage(@Param("page")Page page, @Param("name") String name);
编写sql语句
<select id="selectUserPage" resultType="com.hzy.demo.pojos.User">select *from user2<where><if test="name != null and name != ''">name like '%${name}%'</if></where><if test="page != null">limit #{page.start},#{page.size}</if></select>
测试
@Testvoid test08() {int currentPage = 1; // 当前页int size = 2; // 每页大小Page page = new Page();page.setSize(size);page.setStart((currentPage-1)*size);// 第一页 不匹配姓名List<User> users = userMapper.selectUserPage(page, null);for (User user : users){System.out.println(user);}// 第一页 匹配姓名page.setStart((currentPage-1)*size);users = userMapper.selectUserPage(page, "s");for (User user : users){System.out.println(user);}// 第三页 不匹配姓名currentPage = 3;page.setStart((currentPage-1)*size);users = userMapper.selectUserPage(page, null);for (User user : users){System.out.println(user);}}
2. 使用 RowBounds
RowBounds
是 MyBatis 中用于分页查询的一种简单实现方式,它并不涉及数据库分页查询,而是在查询结果返回后进行截取。
RowBounds 的构造函数需要传入两个参数:
-
offset:偏移量,相当于前面的 start
-
limit:限制数量,相当于前面的 size
RowBounds
的实现原理比较简单,它适用于一些简单的分页需求,但在处理大量数据时可能会导致性能问题,因为所有的数据都会被查询出来,然后在内存中进行截取。对于更复杂的分页需求,可以考虑使用 MyBatis 提供的分页插件或其他更高级的分页方案。
创建查询函数
List<User> selectUserPage(RowBounds rowBounds, @Param("name") String name);
编写 sql
<select id="selectUserPage" resultType="com.hzy.demo.pojos.User">select *from user2<where><if test="name != null and name != ''">name like '%${name}%'</if></where></select>
测试
@Testvoid test09() {int currentPage = 1;int size = 2;RowBounds rowBounds = new RowBounds((currentPage - 1) * size, size);List<User> users = userMapper.selectUserPage(rowBounds, null);for (User user : users) {System.out.println(user);}users = userMapper.selectUserPage(rowBounds, "s");for (User user : users) {System.out.println(user);}}
3. 使用 Mybatis 提供的拦截器机制
MyBatis提供了拦截器(Interceptor)的机制,允许用户在执行SQL语句的过程中进行拦截和干预。拦截器是在执行SQL语句前、后或者代替执行SQL语句的过程中插入自定义的逻辑,从而可以实现一些额外的功能。
一些常见的分页插件比如 PageHelper 也是基于拦截器实现的,这里我们自定义一个拦截器实现分页查询。
3.1 创建一个自定义拦截器类实现 Interceptor
public class PageInterceptor implements Interceptor {// 该方法会在真正的SQL语句执行前后被调用,可以在这里编写拦截逻辑。@Overridepublic Object intercept(Invocation invocation) throws Throwable {return null;}// 用于包装目标对象,返回一个代理对象,该代理对象会拦截目标对象的方法调用。@Overridepublic Object plugin(Object target) {return Interceptor.super.plugin(target);}// 用于设置拦截器的属性,这些属性可以在配置拦截器时传递。@Overridepublic void setProperties(Properties properties) {Interceptor.super.setProperties(properties);}
}
注意要引入 Mybatis 提供的 Interceptor
3.2 创建分页查询函数 与 sql
查询函数
List<User> selectUserPage(@Param("page") Page page, @Param("name") String name);
sql
<select id="selectUserPage" resultType="com.hzy.demo.pojos.User">select *from user2<where><if test="name != null and name != ''">name like '%${name}%'</if></where></select>
3.3 编写拦截逻辑
public Object intercept(Invocation invocation) throws Throwable {}
Invocation
接口是 MyBatis 中拦截器机制中的一个核心接口,用于描述拦截的方法调用。Invocation
接口定义了以下方法:
Object getTarget()
- 获取被拦截的目标对象。在 MyBatis 中,通常是获取到某个 StatementHandler、Executor、ParameterHandler 或 ResultSetHandler 等对象。
Method getMethod()
- 获取被拦截的目标方法。
Object[] getArgs()
- 获取被拦截方法的参数。
Object proceed()
- 调用被拦截方法,相当于执行原始的方法调用。拦截器可以选择是否调用该方法,如果调用,会继续执行原始的方法,如果不调用,可以在拦截器中实现自己的逻辑。
使用 getTarget() 获取目标对象
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
StatementHandler
是 MyBatis 中负责处理 SQL 语句的核心接口,它定义了对数据库的操作方法。在 MyBatis 的执行过程中,StatementHandler
负责创建 PreparedStatement
对象、设置参数、执行 SQL 语句等操作。
可以看到 StatementHandler 对象 里面的 boundSql 对象包含了 sql语句 与 方法参数,利用这个就可以实现分页查询。
拦截逻辑
// @Intercepts 注解配置表明该拦截器会拦截 StatementHandler 接口的 prepare 方法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class })
})
public class PageInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();Map parameterObject = (Map)boundSql.getParameterObject();Page page = (Page) parameterObject.getOrDefault("page",null);if (page != null){// 获取原始 SQL 语句String originalSql = boundSql.getSql();// 修改 SQL 语句StringBuilder sb = new StringBuilder();sb.append(originalSql).append(" limit ").append(page.getStart()).append(",").append(page.getSize());// 将修改后的 SQL 设置回 BoundSqlMetaObject metaObject = SystemMetaObject.forObject(boundSql);metaObject.setValue("sql",sb.toString());}// 继续执行 SQL 语句return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Interceptor.super.plugin(target);}@Overridepublic void setProperties(Properties properties) {Interceptor.super.setProperties(properties);}
}
3.4 注册 PageInterceptor 到 Mybatis 拦截器链中
@Configuration
public class MyBatisConfig {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);// 添加自定义拦截器Interceptor[] interceptors = new Interceptor[]{new PageInterceptor()};sessionFactory.setPlugins(interceptors);return sessionFactory.getObject();}
}
3.5 测试
@Testvoid test10() {int currentPage = 1;int size = 4;Page page = new Page();page.setSize(size);page.setStart((currentPage-1)*size);List<User> users = userMapper.selectUserPage(page, null);for (User user : users){System.out.println(user);} }