mybatis开发一个分页插件、mybatis实现分页、mybatis拦截器
通过官网的mybatis插件说明可知,我们可以通过拦截器进行开发一个插件。
例如这样的:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 开始分页
MagicPage.startPage(1, 3);
// 查询
List<UserEntity> list = mapper.selectAll();
System.out.println(list);
// 获取到分页的内容
PageInfo page = MagicPage.getPage();
System.out.println(page);
格式就是通过线程变量来实现
MagicPage.start(page,size);
// 执行查询
...
MagicPage.getPage();
先编写一个 MagicPage
import cn.hutool.core.lang.Assert;
/**
* @author lingkang
* Created by 2024/3/3
*/
public class MagicPage {
private static final ThreadLocal<PageInfo> local = new ThreadLocal<>();
/**
* 开始分页
*
* @param page 默认 1
* @param size 默认 10
*/
public static void startPage(int page, int size) {
Assert.isTrue(page > 0, "page 最小值为 1");
Assert.isTrue(size > 0, "size 必须大于 0");
PageInfo info = new PageInfo();
info.setSize(size);
info.setPage(page);
local.set(info);
}
public static PageInfo getPage() {
return local.get();
}
public static void removePage() {
local.remove();
}
}
PageInfo
@Data
public class PageInfo {
private int size;
private int page;
private long total;
}
mybatis插件实现:
import cn.hutool.core.convert.BasicType;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 查询分页拦截
*
* @author lingkang
* Created by 2024/3/3
*/
@Intercepts({@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})})
public class MagicPageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
PageInfo page = MagicPage.getPage();
if (page == null)
return invocation.proceed();
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
if (!boundSql.getSql().toLowerCase().contains("select")) {
return invocation.proceed();
}
Connection conn = (Connection) invocation.getArgs()[0];
String totalSql = "select count(*) ";
TotalConvert convert = pageSql(boundSql.getSql());
if (convert.getOrderIndex() != 0)
totalSql += boundSql.getSql().substring(convert.getTotalIndex()) + boundSql.getSql().substring(convert.getOrderIndex());
else
totalSql += boundSql.getSql().substring(convert.getTotalIndex());
PreparedStatement statement = conn.prepareStatement(totalSql);
Object parameterObject = boundSql.getParameterObject();
// 设置参数
if (parameterObject != null) {
if (parameterObject.getClass().isPrimitive() || BasicType.WRAPPER_PRIMITIVE_MAP.containsKey(parameterObject.getClass())) {
statement.setObject(1, parameterObject);
} else if (parameterObject.getClass().isArray()) {
Object[] arr = (Object[]) parameterObject;
for (int i = 1; i <= arr.length; i++)
statement.setObject(i, arr[i]);
} else if (parameterObject instanceof Collection) {
int i = 1;
for (Object o : (Collection) parameterObject)
statement.setObject(i++, o);
} else if (parameterObject instanceof Map) {
Map map = (Map) parameterObject;
int i = 1;
for (ParameterMapping mapping : boundSql.getParameterMappings())
statement.setObject(i++, map.get(mapping.getProperty()));
} else {
// 将它当成对象
int i = 1;
for (ParameterMapping mapping : boundSql.getParameterMappings()) {
Field field = parameterObject.getClass().getDeclaredField(mapping.getProperty());
field.setAccessible(true);
statement.setObject(i++, field.get(parameterObject));
}
}
}
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
long total = resultSet.getLong(1);
page.setTotal(total);
resultSet.close();
}
if (page.getTotal() > 0) {
// 存在分页
String sql = boundSql.getSql() + " limit " + (page.getPage() - 1) * page.getSize() + "," + page.getSize();
Field field = BoundSql.class.getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, sql);
}
return invocation.proceed();
}
Pattern orderBy = Pattern.compile("order\\s+by");
/**
* select id,(select username from user where id=t_order.userid) as username from t_order order by id desc
*/
private TotalConvert pageSql(String sql) {
TotalConvert convert = new TotalConvert();
sql = sql.toLowerCase();
int index = sql.indexOf("from");
int start = sql.substring(0, index).indexOf("(");
while (start != -1) {
start = sql.indexOf("(", start + 1);
if (start == -1)
index = sql.indexOf("from", index + 1);
}
convert.setTotalIndex(index);
// 匹配排序,可能存在排序,需要特殊处理
Matcher matcher = orderBy.matcher(sql);
if (matcher.find()) {
convert.setOrderIndex(matcher.start());
}
return convert;
}
}
需要注意,上面的插件只是做了mysql语法下的分页,如果是sqlserver以及其他数据库需要做调整
配置我们的插件
// 将拦截器添加到配置
configuration.addInterceptor(new MagicPageInterceptor());
执行效果: