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允许客户对以下类的方法进行拦截。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- 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>
对官网教程的解读:
- 用户自定义拦截器类需要实现Interceptor接口。
- 指定拦截器需要拦截的方法。
ExamplePlugin
是对Executor
的update方法进行拦截。查看Executor
源码如下:
int update(MappedStatement ms, Object parameter) throws SQLException; - 配置文件
以下为分页插件的实现,是对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