利用mybatis实现物理分页

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>
时间: 2024-10-08 19:22:17

利用mybatis实现物理分页的相关文章

mybatis的物理分页:mybatis-paginator

github上有一个专门针对mybatis的物理分页开源项目:mybatis-paginator,兼容目前绝大多数主流数据库,十分好用,下面是使用步骤: 环境:struts2 + spring + mybatis 一.pom.xml中添加依赖项 1 <dependency> 2 <groupId>com.github.miemiedev</groupId> 3 <artifactId>mybatis-paginator</artifactId>

IDEA - 利用 Mybatis 使用 maven 命令生成逆向工程

IDEA - 利用 Mybatis 使用 maven 命令生成逆向工程 数据库建表 在本地的数据库建立表,然后利用 Mybatis 使用 maven 命令生成逆向工程. 本地使用 maven 建立一个项目 maven 建立项目,pom.xml 文件配置如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0&qu

Mybatis 数据库物理分页插件 PageHelper

以前使用ibatis/mybatis,都是自己手写sql语句进行物理分页,虽然稍微有点麻烦,但是都习惯了.最近试用了下mybatis的分页插件 PageHelper,感觉还不错吧.记录下其使用方法. 1. 引入依赖jar包: <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5&l

利用mybatis generator插件反向生成Dao、Mapper.xml、pojo(通过maven)

直接进入主题,由于项目选择的利用maven构建,所以选择了利用maven的生成方式.(还有一种可自行百度) 一.在pom.xml中添加插件 <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configur

利用MyBatis Generator自动创建代码

如果你使用过hibernate的eclipse插件自动创建DAO文件,那么就容易理解下面介绍的内容:如果你还没有用过hibernate也无妨.下面介绍使用mybatis 3的eclipse插件自动生成相关文件以及如何使用这些文件. eclipse插件安装地址:http://mybatis.googlecode.com/svn/sub-projects/generator/trunk/eclipse/UpdateSite/ 附件有link安装包,link安装方式参考http://maimode.i

MyBatis学习笔记(3)—— 利用mybatis灌入假数据

由于第三方厂商未能按时提供实时数据,故需要纯手动导入一些实时数据,用于统计分析.正好最近自己学习了mybatis .因此使用mybatis 配置一个select.insert 的简单操作语句,用于灌入实时数据. 业务表 Ems_Standard_FormulaRelation[配置表,主要提供实时表的unitId,mediaId,standardId] Ems_StandardRuntime_Hour[实时表] 准备工作 数据库我用的是sqlserver 2008 r2 1.在lib 中导入所需

Mybatis分页-利用Mybatis Generator插件生成基于数据库方言的分页语句,统计记录总数 (转)

众所周知,Mybatis本身没有提供基于数据库方言的分页功能,而是基于JDBC的游标分页,很容易出现性能问题.网上有很多分页的解决方案,不外乎是基于Mybatis本机的插件机制,通过拦截Sql做分页.但是在像Oracle这样的数据库上,拦截器生成的Sql语句没有变量绑定,而且每次语句的都要去拦截,感觉有点浪费性能. Mybatis Generator是Mybatis的代码生成工具,可以生成大部分的查询语句. 本文提供的分页解决方案是新增Mybatis Generator插件,在用Mybatis

利用mybatis generator 自动创建代码

1.下载mybatis-generator-core-1.3.5 https://github.com/mybatis/generator/releases 2.解压并进入bin目录 3.下载mysql-connector-java-5.1.40-bin.mybatis-3.4.4至bin目录 4.新建src目录和generatorConfig.xml配置文件 1 <?xml version="1.0" encoding="UTF-8"?> 2 <

利用MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件

1. mybatis-generator-core-1.3.5.jar 下载地址:https://github.com/mybatis/generator/releases 2. msyql-connector-java-5.1.30.jar 网上下载. 3. 配置generatorConfig.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration P