码迷,mamicode.com
首页 > 其他好文 > 详细

利用mybatis实现物理分页

时间:2015-04-03 15:21:21      阅读:191      评论:0      收藏:0      [点我收藏+]

标签:

Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量和limit取结果),在大数据量的情况下这样的分页基本上是没有用的。本文基于插件,通过拦截StatementHandler重写sql语句,实现数据库的物理分页。本文适配的mybatis版本是3.2.7具体实现如下:

PaginationInterceptor类:

package org.reacher.interceptor.pagination;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
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.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.RowBounds;
import org.reacher.interceptor.pagination.dialect.Dialect;
import org.reacher.interceptor.pagination.dialect.DialectFactory;
import org.reacher.interceptor.pagination.model.PageBounds;


/**
 * @author reacher
 *
 *	PaginationInterceptor
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationInterceptor implements Interceptor {
	
	private static final Log LOG = LogFactory.getLog(PaginationInterceptor.class);
	
	private Class<?> clazz = null;
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		if(null == this.clazz) {
			LOG.error("Not found has been initialized database dialect!");
			return invocation.proceed();
		}
		final Dialect dialect = DialectFactory.getDialect(this.clazz);
		if(null == dialect) {
			LOG.error(this.clazz.getName() + " initialized failed!");
			return invocation.proceed();
		}
		final StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		final MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
		final RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
		PageBounds pageBounds = null;
		if (rowBounds instanceof PageBounds) {
			pageBounds = (PageBounds) rowBounds;
		}
		if(null == pageBounds || 0 >= pageBounds.getSize() || 0 >= pageBounds.getNumber()) {
			return invocation.proceed();
		}
		final MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
		final BoundSql boundSql = statementHandler.getBoundSql();
		final Object parameterObject = boundSql.getParameterObject();
		final Connection connection = (Connection) invocation.getArgs()[0];
		pageBounds.setCount(this.getCount(mappedStatement, connection, parameterObject, dialect));
		metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitSql(boundSql.getSql(), pageBounds.getSize() * (pageBounds.getNumber() - 1), pageBounds.getSize()));
        metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
		return invocation.proceed();
	}
	
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		String dialectName = properties.getProperty("dialect");
		if(null == dialectName) {
			LOG.error("Property dialectName isn't set!");
			return;
		}
		try {
			this.clazz = Class.forName(dialectName);
		} catch (ClassNotFoundException e) {
			LOG.error(e);
		}
	}
	
	private int getCount(final MappedStatement mappedStatement, final Connection connection, final Object parameterObject, final Dialect dialect) {
		final BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
		final String countSql = dialect.getCountSql(boundSql.getSql());
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		int count = 0;
		try {
			preparedStatement = connection.prepareStatement(countSql);
			final ParameterHandler handler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
			handler.setParameters(preparedStatement);
			resultSet = preparedStatement.executeQuery();
			if (resultSet.next()) {
				count = resultSet.getInt(1);
			}
		} catch (Exception e) {
			LOG.error(e);
		} finally {
			if (preparedStatement != null) {
				try {
					preparedStatement.close();
				} catch (SQLException e) {
					LOG.error(e);
				}
			}
		}
		return count;
	}

}
Dialect接口:

package org.reacher.interceptor.pagination.dialect;

/**
 * @author reacher
 *
 */
public interface Dialect {
	
	/**
	 * 检查数据库是否支持分页
	 */
	public abstract boolean supportsLimit();
	
	/**
	 * 得到获取数据总条数的SQL语句
	 */
	public String getCountSql(String sql);	
	/**
	 * 得到分页的SQL语句
	 */
	public abstract String getLimitSql(String sql, long offset, long limit);
}
DialectFactory工厂类:
package org.reacher.interceptor.pagination.dialect;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * @author reacher
 *
 */
public final class DialectFactory {
	
	private static Map<String, Dialect> DIALECTS = new HashMap<String, Dialect>();
	
	public static Dialect getDialect(Class<?> clazz) throws Exception {
		Dialect dialect = DIALECTS.get(clazz.getSimpleName());
		if (dialect == null) {
			dialect = newInstance(clazz);
			if(null != dialect) {
				DIALECTS.put(clazz.getSimpleName(), dialect);
			}
		}
		return dialect;
	}
	
	private static Dialect newInstance(Class<?> clazz) throws Exception {
		if(null == clazz) {
			return null;
		}
		Constructor<?> constructor = clazz.getConstructor();
		constructor.setAccessible(true);
		Object object = constructor.newInstance();
		Dialect dialect = null;
		if(object instanceof Dialect) {
			dialect = (Dialect)object;
		}
		return dialect;
		
	}
}
PageBounds类:

package org.reacher.interceptor.pagination.model;

import org.apache.ibatis.session.RowBounds;

/**
 * @author reacher
 *
 */
public class PageBounds extends RowBounds {
	
	private int size;//页面大小

	private int number;//当前页数

	private int count;//数据总条数
	
	private int total;//总页数

	public PageBounds() {
		this.number = 1;
		this.size = 10;
	}

	public PageBounds(int pageSize) {
		this.number = 1;
		this.size = pageSize;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public int getTotal() {
		this.total = this.count / this.size + (this.count % this.size > 0 ? 1 : 0); 
		return this.total;
	}

	public void setTotal(int total) {
		this.total = total;
	}

}
实现mysql数据库的分页具体如下:

MySQLDialect类:

package org.reacher.interceptor.pagination.dialect.database;

import org.reacher.interceptor.pagination.dialect.Dialect;

/**
 * @author reacher
 *
 */
public class MySQLDialect implements Dialect {
	
	@Override
	public boolean supportsLimit() {
		return true;
	}
	
	@Override
	public String getCountSql(String sql) {
		return "SELECT COUNT(*) FROM (" + sql.replaceAll(";", "") + ") temp";
	}

	@Override
	public String getLimitSql(String sql, long offset, long limit) {
		StringBuffer stringBuffer = new StringBuffer(sql.replaceAll(";", ""));
		stringBuffer.append(" LIMIT ").append(offset).append(", ").append(limit);
		return stringBuffer.toString();
	}

}
mybatis-configuration.xml配置如下:

<plugins>
<span style="white-space:pre">	</span><plugin	interceptor="org.reacher.interceptor.pagination.PaginationInterceptor">
		<property name="dialect" value="org.reacher.interceptor.pagination.dialect.database.MySQLDialect"/>
	</plugin>
</plugins>


利用mybatis实现物理分页

标签:

原文地址:http://blog.csdn.net/awen_c/article/details/44855039

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!