mybatis分页插件哪个最好

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis分页插件哪个最好相关的知识,希望对你有一定的参考价值。

使用方法
1. 引入分页插件
引入分页插件一共有下面2种方式,推荐使用Maven方式,这种方式方便更新。
1). 引入Jar包
如果你想使用本项目的jar包而不是直接引入类,你可以在这里下载各个版本的jar包(点击Download下的jar即可下载)
https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/
http://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/
由于使用了sql解析工具,你还需要下载jsqlparser.jar(这个文件完全独立,不依赖其他):
http://repo1.maven.org/maven2/com/github/jsqlparser/jsqlparser/0.9.1/
http://git.oschina.net/free/Mybatis_PageHelper/attach_files
2). 使用maven
添加如下依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.6.0</version>
</dependency>

当使用maven中央库中的快照版(带"-SNAPSHOT"的版本)时,需要在pom.xml中添加如下配置:
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

2. 在Mybatis配置xml中配置拦截器插件:
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样-->
<property name="offsetAsPageNum" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
<property name="pageSizeZero" value="true"/>
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true"/>
<!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
<!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
<property name="params" value="pageNum=start;pageSize=limit;pageSizeZero=zero;reasonable=heli;count=contsql"/>
</plugin>
</plugins>

这里的com.github.pagehelper.PageHelper使用完整的类路径。
其他五个参数说明:
增加dialect属性,使用时必须指定该属性,可选值为oracle,mysql,mariadb,sqlite,hsqldb,postgresql,sqlserver,没有默认值,必须指定该属性。
增加offsetAsPageNum属性,默认值为false,使用默认值时不需要增加该配置,需要设为true时,需要配置该参数。当该参数设置为true时,使用RowBounds分页时,会将offset参数当成pageNum使用,可以用页码和页面大小两个参数进行分页。
增加rowBoundsWithCount属性,默认值为false,使用默认值时不需要增加该配置,需要设为true时,需要配置该参数。当该参数设置为true时,使用RowBounds分页会进行count查询。
增加pageSizeZero属性,默认值为false,使用默认值时不需要增加该配置,需要设为true时,需要配置该参数。当该参数设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page类型)。
增加reasonable属性,默认值为false,使用默认值时不需要增加该配置,需要设为true时,需要配置该参数。具体作用请看上面配置文件中的注释内容。
为了支持startPage(Object params)方法,增加了一个params参数来配置参数映射,用于从Map或ServletRequest中取值,可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值。
参考技术A

mybatis-helper最简单的分页插件,你可以用用这个,超级简单

MyBatis分页插件实现

日常开发中,MyBatis已经成为数据持久层实现的重要角色,以下就是一个使用MyBatis开发的一个分页插件的实现。关于Mybatis的插件概念可以查看MyBatis官网

技术分享图片

查看官网教程可以得知,MyBatis允许客户对以下类的方法进行拦截。

  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (prepare, parameterize, batch, update, query)

使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

对官网教程的解读:

  1. 用户自定义拦截器类需要实现Interceptor接口。
  2. 指定拦截器需要拦截的方法。ExamplePlugin是对Executor的update方法进行拦截。查看Executor源码如下:
    int update(MappedStatement ms, Object parameter) throws SQLException;
  3. 配置文件

以下为分页插件的实现,是对Executor
Java <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;方法的拦截。

1.定义拦截器类,实现Interceptor接口,指定拦截的方法。

@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class PageInterceptor implements Interceptor {
    static int MAPPED_STATEMENT_INDEX = 0;
    static int PARAMETER_INDEX = 1;
    static int ROWBOUNDS_INDEX = 2;
    static int RESULT_HANDLER_INDEX = 3;

    Dialect dialect;

    public Object intercept(Invocation invocation) throws Throwable {
        processIntercept(invocation.getArgs());
        return invocation.proceed();
    }

    void processIntercept(final Object[] queryArgs) {
        MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
        Object parameter = queryArgs[PARAMETER_INDEX];
        // 获取分页信息RowBounds
        final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX];
        int pageCurrent = rowBounds.getOffset();
        int pageSize = rowBounds.getLimit();

        // 获取NAMESPACE和方法
        String[] nameSpaceId = ms.getId().split("\\.");
        if (!ArrayUtils.isEmpty(nameSpaceId) && nameSpaceId[nameSpaceId.length - 1].startsWith("query")) {
            if (dialect.supportsLimit() && (pageCurrent != RowBounds.NO_ROW_OFFSET || pageSize != RowBounds.NO_ROW_LIMIT)) {
                BoundSql boundSql = ms.getBoundSql(parameter);
                String sql = boundSql.getSql().trim();
                if (dialect.supportsLimitOffset()) {
                    sql = dialect.getPageSql(sql, pageCurrent, pageSize);
                    pageCurrent = RowBounds.NO_ROW_OFFSET;
                } else {
                    sql = dialect.getPageSql(sql, 0, pageSize);
                }
                pageSize = RowBounds.NO_ROW_LIMIT;

                queryArgs[ROWBOUNDS_INDEX] = new RowBounds(pageCurrent, pageSize);
                BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
                MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
                
                //需要将metaParameters赋值过去..  
                MetaObject countBsObject = SystemMetaObject.forObject(newBoundSql);  
                MetaObject boundSqlObject = SystemMetaObject.forObject(boundSql);  
                countBsObject.setValue("metaParameters",boundSqlObject.getValue("metaParameters"));  
                
                queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
            }
        }
    }

    /** @see MapperBuilderAssistant */
    private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());

        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        builder.keyProperty(ms.getKeyProperties() == null ? null : ms.getKeyProperties()[0]);

        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());

        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        try { 
            String dialectClassValue  =properties.getProperty("dialectClass");
            if("com.page.dialect.MySqlDialect".equals(dialectClassValue)){
                dialect = (Dialect) Class.forName(MySqlDialect.class.getCanonicalName()).newInstance();
            }else{
                dialect = (Dialect) Class.forName(OracleDialect.class.getCanonicalName()).newInstance();
            }
        } catch (Exception e) {
            throw new RuntimeException("cannot create dialect instance by dialectClass : " + OracleDialect.class.getCanonicalName(), e);
        }
    }

    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}

/**
 * 类似HIBERNATE的Dialect,但只精简出分页部分
 */
public class Dialect {

    /**
     * 将SQL变成分页SQL语句</br> 
     * 拆分原始SQL,拼装需要的分页SQL.
     */
    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        throw new UnsupportedOperationException("paged queries not supported");
    }

    public boolean supportsLimit() {
        return false;
    }

    public boolean supportsLimitOffset() {
        return supportsLimit();
    }
}

public class MySqlDialect extends Dialect {

    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        // 原始SQL
        String orgSql = sql.trim();
        // 重置当前页
        pageCurrent = pageCurrent - 1;
        //起始数据行
        int offset=pageCurrent*pageSize;
        // 最终SQL
        StringBuffer finaleSql = new StringBuffer();
        
        finaleSql.append("select * from ( ");
        

        // 追加原始SQL
        finaleSql.append(orgSql);

        //用limit分页
        finaleSql.append(" limit "+String.valueOf(offset)+","+String.valueOf(pageSize));
         
        
        finaleSql.append("  ) as tabletotal");
        
    
        return finaleSql.toString();
    }

    public boolean supportsLimit() {
        return true;
    }

    public boolean supportsLimitOffset() {
        return true;
    }
}


public class OracleDialect extends Dialect {

    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        // 原始SQL
        String orgSql = sql.trim();
        // 重置当前页
        pageCurrent = pageCurrent - 1;

        // 最终SQL
        StringBuffer finaleSql = new StringBuffer();
        // 如果当前页>0
        finaleSql.append("select * from (select * from ( select row_.*, rownum rownum_ from ( ");

        // 追加原始SQL
        finaleSql.append(orgSql);

        // 如果当前页>0
        if (pageCurrent > 0) {
            finaleSql.append(" ) row_ )) where rownum_ <= " + ((pageCurrent * pageSize) + "+" + pageSize) + " and rownum_ > "
                    + String.valueOf(pageCurrent * pageSize));
        } else {// 如果当前页<=0
            finaleSql.append(" ) row_ )) where rownum_ <= " + String.valueOf(pageSize));
        }

        return finaleSql.toString();
    }

    public boolean supportsLimit() {
        return true;
    }

    public boolean supportsLimitOffset() {
        return true;
    }
}



public class SystemMetaObject {
    public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY,
            DEFAULT_OBJECT_WRAPPER_FACTORY);

    private SystemMetaObject() {
        // Prevent Instantiation of Static Class
    }

    private static class NullObject {
    }

    public static MetaObject forObject(Object object) {
        return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
    }
}
<configuration>
    <properties>
        <property name="dialect" value="oracle" />
    </properties>
    <settings>
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="jdbcTypeForNull" value="VARCHAR" />
    </settings>
    <plugins>
        <plugin interceptor="com.page.interceptor.PageInterceptor">
            <property name="dialectClass" value="com..page.dialect.OracleDialect" />
        </plugin>
    </plugins>
</configuration>


以上是关于mybatis分页插件哪个最好的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis分页插件:PageHelper和PageInfo

Mybatis分页插件

SpringBoot+Mybatis配置Pagehelper分页插件实现自动分页

Mybatis分页插件

七:MyBatis逆向工程&分页插件

七:MyBatis逆向工程&分页插件