网上有很多MyBatis物理分页插件,基本都是围绕拦截指定分页对象来处理的;网上分页逻辑为,先查出总的列表数,使用分页插件查出分页后列表集合,封装总列表数和分页列表集合到Page结果对象;用起来还要自己封装结果类,相对比较麻烦;我想省去这些复杂的步骤,直接传递分页对象,返回结果对象。
需求:
这里我想像JPA一样传递Pagination分页对象进去,返回Page结果对象。
逻辑:
MyBatis预处理拦截器拦截指定的Pagination,并重写当前的SQL为分页SQL
MyBatis结果集拦截器拦截指定的Pagination,根据分页SQL得到查询总量的SQL执行得到总的列表数,得到原分页后的列表集合,封装为Page对象并返回
代码类:
PaginationStatementInterceptor类为SQL预处理拦截类,拦截指定的分页类,这里我使用的是org.springframework.data.domain.Pageable分页类(Spring-data-commons包下的,如果用Spring-data应该会附带出来这个包的);如果MyBatis中分页方法有这个分页类,则拦截并修改原来的执行SQL为想要的分页SQL;以MySQL为例比如:之前的SQL为 select * from user 修改后的SQL为 select * from user limit 100, 10利用反射替换原来SQL值的位置delegate.boundSql.sql
下面举例用的方言为MySQL对应的类MySql5Dialect继承了Dialect类,该类的getLimitString得到分页SQL,如果是别的数据库继承Dialect类,重写getLimitString即可
import com.mm.persist.expands.mybatis.dialect.Dialect;
import com.mm.persist.expands.mybatis.dialect.MySql5Dialect;
import com.mm.persist.expands.mybatis.dialect.OracleDialect;
import com.mm.persist.expands.mybatis.dialect.SQLServer2005Dialect;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.binding.MapperMethod;
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.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.springframework.data.domain.Pageable;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationStatementInterceptor implements Interceptor {
private final static Log log = LogFactory
.getLog(PaginationStatementInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
ParameterHandler parameterHandler = statementHandler.getParameterHandler();
Object parameterObject = parameterHandler.getParameterObject();
Pageable pagination = null;
if(parameterObject instanceof MapperMethod.ParamMap){
MapperMethod.ParamMap paramMapObject = (MapperMethod.ParamMap)parameterObject ;
if(paramMapObject != null){
for(Object key : paramMapObject.keySet()){
if(paramMapObject.get(key) instanceof Pageable){
pagination = (Pageable) paramMapObject.get(key);
break;
}
}
}
}
if (pagination != null) {
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
Dialect.Type databaseType = null;
try {
databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
} catch (Exception e) {
throw new Exception("Generate SQL: Obtain DatabaseType Failed!");
}
Dialect dialect = null;
switch (databaseType) {
case MYSQL:
dialect = new MySql5Dialect();
break;
case ORACLE:
dialect = new OracleDialect();
break;
case SQLSERVER:
dialect = new SQLServer2005Dialect();
break;
}
String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, pagination.getPageNumber() * pagination.getPageSize(), pagination.getPageSize()));
if (log.isDebugEnabled()) {
BoundSql boundSql = statementHandler.getBoundSql();
log.debug("Generate SQL : " + boundSql.getSql());
}
return invocation.proceed();
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
PaginationResultSetInterceptor类为结果拦截类,拦截指定的分页类,这里我使用的是org.springframework.data.domain.Pageable分页类(Spring-data-commons包下的,如果用Spring-data应该会附带出来这个包的);如果MyBatis中分页方法有这个分页类,则拦截结果集进行重写得到最终想要的org.springframework.data.domain.Page结果类(Spring-data-commons包下的,如果用Spring-data应该会附带出来这个包的);Page中会包含分页信息和分页后的列表信息。
下面举例用的方言为MySQL对应的类MySql5Dialect继承了Dialect类,该类的getCountString方法是根据分页SQL解析获取到对应的查询总记录数的SQL,如果是别的数据库继承Dialect类,重写getCountString即可
invocation.proceed()为原分页结果集,这里根据上面的查询总记录数的SQL执行获取总记录数结果,封装Page对象设置原结果集为分页结果列表,设置总记录数,并返回
import com.mm.persist.expands.mybatis.dialect.Dialect;
import com.mm.persist.expands.mybatis.dialect.MySql5Dialect;
import com.mm.persist.expands.mybatis.dialect.OracleDialect;
import com.mm.persist.expands.mybatis.dialect.SQLServer2005Dialect;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.support.JdbcUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class PaginationResultSetInterceptor implements Interceptor {
private final static Log log = LogFactory.getLog(PaginationResultSetInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
DefaultResultSetHandler resultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
MetaObject metaResultSetHandler = MetaObject.forObject(resultSetHandler, new DefaultObjectFactory(), new DefaultObjectWrapperFactory());
try {
ParameterHandler parameterHandler = (ParameterHandler) metaResultSetHandler.getValue("parameterHandler");
Object parameterObject = parameterHandler.getParameterObject();
Pageable pagination = null;
if(parameterObject instanceof MapperMethod.ParamMap){
MapperMethod.ParamMap paramMapObject = (MapperMethod.ParamMap)parameterObject ;
if(paramMapObject != null){
for(Object key : paramMapObject.keySet()){
if(paramMapObject.get(key) instanceof Pageable){
pagination = (Pageable) paramMapObject.get(key);
break;
}
}
}
}
if (pagination != null) {
BoundSql boundSql = (BoundSql) metaResultSetHandler.getValue("parameterHandler.boundSql");
Configuration configuration = (Configuration) metaResultSetHandler.getValue("configuration");
Connection connection = (Connection) metaResultSetHandler.getValue("executor.delegate.transaction.connection");
String originalSql = boundSql.getSql();
Dialect.Type databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
Dialect dialect = null;
switch (databaseType) {
case MYSQL:
dialect = new MySql5Dialect();
break;
case ORACLE:
dialect = new OracleDialect();
break;
case SQLSERVER:
dialect = new SQLServer2005Dialect();
break;
}
// 修改sql,用于返回总记录数
String sql = dialect.getCountString(originalSql);
Long totalRecord = getTotalRecord(connection, sql, parameterHandler);
Object result = invocation.proceed();
Page page = new PageImpl((List)result, pagination, totalRecord);
// // 设置返回对象类型
// metaResultSetHandler.setValue("mappedStatement.resultMaps[0].type.name", Page.class.getName());
// 设置返回值
List<Page> pageList = new ArrayList<Page>();
pageList.add(page);
return pageList;
}
} catch (Exception e) {
throw new Exception("Overwrite SQL : Fail!");
}
return invocation.proceed();
}
/**
* 执行 count 操作
* @param connection 数据库连接
* @param sql sql
* @param parameterHandler 参数设置处理器
* @return
*/
private Long getTotalRecord(Connection connection,String sql,ParameterHandler parameterHandler){
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
parameterHandler.setParameters(preparedStatement);
resultSet = preparedStatement.executeQuery();
resultSet.next();
return (Long) JdbcUtils.getResultSetValue(resultSet, 1, Long.class);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.closeResultSet(resultSet);
JdbcUtils.closeStatement(preparedStatement);
}
return 0l;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
这里我仅以MySQL为例,其他需要同学们自己写
public class MySql5Dialect extends Dialect {
public String getLimitString(String querySqlString, int offset, int limit) {
return querySqlString + " limit " + offset + " ," + limit;
}
@Override
public String getCountString(String querySqlString) {
int limitIndex = querySqlString.lastIndexOf("limit");
if(limitIndex != -1){
querySqlString = querySqlString.substring(0, limitIndex != -1 ? limitIndex : querySqlString.length() - 1);
}
// 用的过程中会发现这里对原有sql进行包装一层select count会有SQL效率低的问题
// 等待优化
return "SELECT COUNT(*) FROM (" + querySqlString + ") tem";
}
public boolean supportsLimit() {
return true;
}
}
public abstract class Dialect {
public static enum Type {
MYSQL, ORACLE, SQLSERVER
}
public abstract String getLimitString(String querySqlString, int offset, int limit);
public abstract String getCountString(String querySqlString);
}
以上代码测试并在工作项目中运用,代码可能不全,有时间做个demo并上传
参考文档: http://mybatis.github.io/mybatis-3/zh/configuration.html#plugins
分享到:
相关推荐
而“mybatis物理分页插件”是针对MyBatis设计的一个扩展,用于解决在大数据量查询时的性能问题,通过实现物理分页来避免内存溢出。 物理分页是指在数据库层面进行分页,相比于逻辑分页(在应用层进行数据截取),...
06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo...
3. **分页插件**:PageHelper是常用的MyBatis分页插件,它能自动处理物理分页。在项目的pom.xml中引入PageHelper依赖,然后在MyBatis的配置文件中启用插件,并设置相关属性,如dialect(数据库类型)。 4. **Spring...
自己写的一个mybatis物理分页插件,支持mysql,oracle,db2,ms sql server2005-2008和ms sql server2012, 由于sql server2005的分页比较独特, 暂时只支持单order by 的情况, 多个order by会报错 , mysql,oracle,db2完美...
Mybatis通用分页插件是Java开发中广泛使用的ORM(对象关系映射)框架扩展,主要针对Mybatis进行优化,提供了高效便捷的分页功能。这个插件的目的是简化在数据库查询时的分页操作,使得开发者能够更专注于业务逻辑,...
这个插件专门为MyBatis设计,旨在提供高性能的物理分页解决方案。 首先,物理分页与逻辑分页的区别在于,物理分页直接在数据库层面进行,通过SQL语句的LIMIT和OFFSET或者ROWNUM等关键字来限制返回的数据量,避免了...
物理分页是数据库直接在存储层执行的分页操作,它根据指定的页码和每页大小,直接在数据库查询时返回相应范围的数据。这种方法的优点在于,它只检索需要显示的数据,减少了网络传输的数据量,提高了查询效率。 在...
【标题】"mybatis分页插件代码"主要涉及到MyBatis框架中的一种增强功能——分页插件的使用。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。在处理大数据量时,分页查询是必不可少的优化...
本教程将详细讲解如何在Spring Boot项目中集成MyBatis,并利用分页插件实现高效的数据分页。 首先,我们需要在Spring Boot项目中引入MyBatis依赖。在`pom.xml`文件中添加以下Maven依赖: ```xml <groupId>org....
MyBatis 分页插件是针对 MyBatis 框架设计的一款强大的辅助工具,它极大地简化了在数据库查询时的分页操作。在没有分页插件的情况下,开发者需要手动编写分页相关的 SQL 语句,这既繁琐又容易出错。而 PageHelper ...
<... PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- value="mssql|oracle|mysql|db2" --> </configuration>
PageHelper会返回一个Page对象,包含了查询结果和分页信息。 ```java PageHelper.startPage(pageNum, pageSize); List<User> users = userMapper.selectAll(); PageInfo<User> pageInfo = new PageInfo(users); ``...
针对这一问题,Mybatis分页插件——PageHelper应运而生,它简化了分页操作的过程,提高了开发效率。该插件目前支持多种主流数据库如Oracle、MySQL、MariaDB、SQLite、Hsqldb以及PostgreSQL。 #### 二、PageHelper的...
自定义分页插件提供了一种灵活且高效的方式来实现 MyBatis 的分页查询。通过实现 Interceptor 接口并注册插件,我们可以根据不同的业务需求和数据库特性来定制分页逻辑。本文详细介绍了自定义分页插件的实现步骤和...
分页插件PageHelper是通过mybatis的拦截器实现分页功能的,拦截sql查询请求,添加分页语句,最终实现分页查询功能。 一、分页插件PageHelper支持的数据库类型? Oracle,MySql,MariaDB,SQLite等 二、分页插件...
mybatis集成了分页的插件,采用springmvc+spring+mybatis或者springboot+mybatis的时候可以无缝对接使用
MyBatis分页插件是数据库操作中常用的一种工具,它可以帮助开发者在使用MyBatis框架进行数据查询时实现高效且便捷的分页功能。在Java Web开发中,当需要处理大量的数据时,分页显示不仅可以提高用户体验,也能减轻...
mybatis分页插件支持查询~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~