Mybatis的自带分页方法只是逻辑分页,如果数据量很大,内存会溢出,不知道为什么开源组织不在里面实现类似Hibernate的物理分页处理方法。在不改动Mybatis源代码的情况下,怎么使Mybatis支持物理分页呢?下面我们来看看。
(1)新建一个Java类Dialect.java,该类的内容如下:
package org.mybatis.extend.interceptor;
public abstract class Dialect {
public static enum Type{
MYSQL,
ORACLE
}
public abstract String getLimitString(String sql, int skipResults, int maxResults);
}
(2)新建一个Java类OracleDialect.java,该类继承Dialect 类,具体的内容如下:
package org.mybatis.extend.interceptor;
public class OracleDialect extends Dialect{
/* (non-Javadoc)
* @see org.mybatis.extend.interceptor.IDialect#getLimitString(java.lang.String, int, int)
*/
@Override
public String getLimitString(String sql, int offset, int limit) {
sql = sql.trim();
StringBuffer pagingSelect = new StringBuffer(sql.length() + 100 );
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
pagingSelect.append(sql);
pagingSelect.append(" ) row_ ) where rownum_ > ").append(offset).append( " and rownum_ <= ").append(offset + limit);
return pagingSelect.toString();
}
}
(3)新建一个Mybaits的拦截器PaginationInterceptor.java,实现Interceptor接口,该类的内容如下:
package org.mybatis.extend.interceptor
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.DefaultParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import platform.pages.mybatis.dialects.MysqlDialect;
import platform.pages.mybatis.dialects.OracleDialect;
/**
* Mybaits的拦截器
*
* @author fhx 2013-1-27 下午03:04:33
*/
@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
protected static Logger log = LoggerFactory.getLogger(PaginationInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql2 = statementHandler.getBoundSql();
List list = boundSql2.getParameterMappings();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);
RowBounds rowBounds = (RowBounds) boundSql2.getParameterObject();
if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
return invocation.proceed();
}
String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
Dialect.Type databaseType = null;
try {
databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
} catch (Exception e) {
System.out.println("mybatis-config.xml中未设置数据库类型");
}
if (databaseType == null) {
throw new RuntimeException(
"the value of the dialect property in configuration.xml is not defined : " + configuration.getVariables().getProperty("dialect"));
}
Dialect dialect = null;
switch (databaseType) {
case ORACLE: // oracle 分页
dialect = new OracleDialect();
break;
case MYSQL: // MySQL分页
dialect = new MysqlDialect();
break;
}
metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()));
metaStatementHandler.setValue("delegate.rowBounds.offset",RowBounds.NO_ROW_OFFSET);
metaStatementHandler.setValue("delegate.rowBounds.limit",RowBounds.NO_ROW_LIMIT);
if (log.isDebugEnabled()) {
BoundSql boundSql = statementHandler.getBoundSql();
log.debug(" 生成分页SQL : " + boundSql.getSql());
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties arg0) {
}
}
(4)将Mybatis的拦截器配置到Mybatis的全局配置文件(mybatis.cfg.xml)中,具体如下:
<?xml version="1.0" encoding= "UTF-8" ?>
<!DOCTYPE configuration PUBLIC
"-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties>
<property name="dialect" value= "oracle"/>
</properties>
<plugins>
<plugin interceptor="org.mybatis.extend.interceptor.PaginationInterceptor"/>
</plugins>
</configuration>
(5)使用方法同Mybatis逻辑分页一样,拦截器会自动拦截执行SQL的地方,加上分页代码:
getSqlSession().selectList(sqlId, paramMap,new RowBounds(pageId, pageSize));
(6) 和spring集成封装有superDao
public class SupperDao extends SqlSessionDaoSupport{
protected Logger log = LoggerFactory.getLogger(getClass());
/**
* 保存
* @param key
* @param object
*/
public void save(String key, Object object) {
getSqlSession().insert(key, object);
}
/**
* 删除
* @param key
* @param id
*/
public void delete(String key, Serializable id) {
getSqlSession().delete(key, id);
}
/**
* 删除
* @param key
* @param object
*/
public void delete(String key, Object object) {
getSqlSession().delete(key, object);
}
/**
* 查询 返回一个结果
* @param <T>
* @param key
* @param params
* @return
*/
public <T> T get(String key, Object params) {
return (T) getSqlSession().selectOne(key, params);
}
/**
* 查询 返回多个结果
* @param <T>
* @param key
* @return
*/
public <T> List<T> findList(String key) {
return getSqlSession().selectList(key);
}
/**
* 分页查询
* @param <T>
* @param key
* @param offset
* @param pageSize
* @return
*/
public <T> List<T> findList(String key,int offset,int pageSize) {
return getSqlSession().selectList(key,new RowBounds(offset, pageSize));
}
/**
* 查询 可带参数 返回多个结果
* @param <T>
* @param key
* @param params
* @return
*/
public <T> List<T> findList(String key, Object params) {
return getSqlSession().selectList(key, params);
}
/**
* 分页查询 可带参数
* @param <T>
* @param key
* @param params
* @param pageNo
* @param pageSize
* @return
*/
public <T> List<T> findList(String key, Object params,int pageOffset,int pageSize) {
return getSqlSession().selectList(key, params,new RowBounds(pageOffset, pageSize));
}