文章目录
- 一、分页插件功能特性
- 二、设计一个基本的分页插件
- 1、缺陷分析
- 三、分享一个分页插件:
- 写在后面
一、分页插件功能特性
1.易用性:不需要额外配置,参数中带上Page即可,Page尽可能简单。
2.不对使用场景作假设:不限制用户使用,如接口调用,还是回话调用,又或是对Executor以及StatementHandler的选择等,不能影响缓存业务。
3.友好性:当不符合分页情况下,作出友好的用户提示。如在修改操作中传入分页参数,或用户本身已在查询语句中自带分页语句,这种情况应作出提示(抛异常)。
二、设计一个基本的分页插件
public class Page {
private int total; // 总行数
private int size; // 每页大小
private int index; // 页码,从1开始
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
// offset 0 limit 50
public int getOffset(){
return size * (index -1);
}
}
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
/**
* 分页拦截器
*/
@Intercepts(
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
)
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1.检测当前是否满足分页条件
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// sql包(sql、参数、参数映射)
BoundSql boundSql = statementHandler.getBoundSql();
// 参数(单个参数时就是参数类型,多个参数的话会转成map)
Object parameterObject = boundSql.getParameterObject();
// 查出是否有Page
Page page = null;
if(parameterObject instanceof Page){
// 参数就是Page
page = (Page) parameterObject;
} else if(parameterObject instanceof Map) {
// 多个参数的情况下需要遍历出Page
page = (Page)((Map) parameterObject).values().stream().filter(v -> v instanceof Page).findFirst().orElse(null);
}
if(page == null){
return invocation.proceed();
}
// 2.设置总行数
page.setTotal(selectCount(invocation));
// 3.修改原有SQL
String newSql = String.format("%s limit %s offset %s", boundSql.getSql(), page.getSize(), page.getOffset());
SystemMetaObject.forObject(boundSql).setValue("sql", newSql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 配置分页插件时的配置
}
/**
* 查询总数
*/
private int selectCount(Invocation invocation) throws SQLException {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
// count语句
String countSql = String.format("select count(*) from (%s) as _page", boundSql.getSql());
Connection connection = (Connection) invocation.getArgs()[0];
ParameterHandler parameterHandler = statementHandler.getParameterHandler();
return executeCount(parameterHandler, connection, countSql);
}
private int executeCount(ParameterHandler parameterHandler, Connection connection, String countSql) {
try (PreparedStatement stmt = prepareStatement(parameterHandler, connection, countSql);
ResultSet rs = stmt.executeQuery()) {
return rs.next() ? unboxing(rs.getInt(1), 0) : 0;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private PreparedStatement prepareStatement(ParameterHandler parameterHandler, Connection connection, String countSql) throws SQLException {
PreparedStatement prepareStatement = connection.prepareStatement(countSql);
parameterHandler.setParameters(prepareStatement);
return prepareStatement;
}
private int unboxing(Integer value, int defaultValue) {
return value != null ? value : defaultValue;
}
}
1、缺陷分析
对二级缓存不友好,并且如果XML中带有collection标签的话,会造成数据行数紊乱。
三、分享一个分页插件:
pageHelper
写在后面
如果本文对你有帮助,请点赞收藏关注一下吧 ~