`

Mybatis使用篇之十二:实现物理分页

阅读更多

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.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;


@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PaginationInterceptor implements Interceptor {
	//日志对象
	protected static Logger log = LoggerFactory.getLogger(PaginationInterceptor.class);
	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);
		
		RowBounds rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
		if(rowBounds == null || rowBounds == RowBounds.DEFAULT){
			return invocation.proceed();
		}
		
		DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler)metaStatementHandler.getValue("delegate.parameterHandler");
		Map parameterMap = (Map)defaultParameterHandler.getParameterObject();
		Object sidx = parameterMap.get("_sidx");
		Object sord = parameterMap.get("_sord");
		
		String originalSql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
		
		if(sidx != null && sord != null){
			originalSql = originalSql + " order by " + sidx + " " + sord;
		}
		
		Configuration configuration = (Configuration)metaStatementHandler.getValue("delegate.configuration");
        
                            Dialect.Type databaseType  = null;
		try{
			databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
		} catch(Exception e){
			//ignore
		}
		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:
				dialect = new OracleDialect();
				break;
			case MYSQL://需要实现MySQL的分页逻辑
				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();
	}

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
	 */
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	/* (non-Javadoc)
	 * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
	 */
	@Override
	public void setProperties(Properties arg0) {
		// TODO Auto-generated method stub
		
	}

}

 

 (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));

 

 

分享到:
评论
7 楼 Dreamer_1029 2014-09-27  
阿里内部看到的url,建议楼主继续补全
6 楼 DR-YangLong 2013-12-20  
不用那么麻烦,求关注http://dr-yanglong.iteye.com/blog/1992383
5 楼 sunney2010 2013-02-02  
进入PaginationInterceptor之前就报错了。在全局配置去了这个拦截器就正常,求解,错误为:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: java.lang.NullPointerException
### Cause: java.lang.NullPointerException
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:365)
at $Proxy14.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:202)
at com.sunney.userInfo.dao.Impl.UserInfoDaoImpl.queryUserInfo(UserInfoDaoImpl.java:48)
at com.sunney.userInfo.service.Impl.UserInfoServiceImpl.queryUserInfo(UserInfoServiceImpl.java:39)
at com.sunney.userInfo.service.Impl.UserInfoServiceImpl$$FastClassByCGLIB$$2d83dad1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.sunney.userInfo.service.Impl.UserInfoServiceImpl$$EnhancerByCGLIB$$9e960c26.queryUserInfo(<generated>)
at com.sunney.userInfo.UserInfoTest.queryUserInfoTest(UserInfoTest.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: java.lang.NullPointerException
### Cause: java.lang.NullPointerException
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:355)
... 43 more
Caused by: java.lang.NullPointerException
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:101)
... 48 more

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:591)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:297)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:192)
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:396)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:91)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

4 楼 sunney2010 2013-02-02  
进入PaginationInterceptor之前就报错了。求解:
3 楼 flynofry 2012-12-11  
您好!参考/转载文章
http://www.cnblogs.com/jcli/archive/2011/08/09/2132222.html
其实可以标注一下出处。

2 楼 say4ever2u 2012-11-22  
say4ever2u 写道
第41parameterMap 没做校验就直接使用, 40行 有可能取到null


我确实不明白 41 42这两行 是干什么的  _sidx _sord是干什么用的, 调试也没有发现他们,所以 报错,呵呵 希望给于说明下
1 楼 say4ever2u 2012-11-22  
第41parameterMap 没做校验就直接使用, 40行 有可能取到null

相关推荐

    mybatis分页配置

    1. **基于插件的分页**:MyBatis 提供了一个 PageHelper 插件,它可以方便地实现物理分页。首先,你需要在 Maven 或 Gradle 的依赖中添加 PageHelper 的依赖,然后在 MyBatis 的配置文件中启用该插件。PageHelper ...

    springmybatis

    mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in action之八mybatis 动态sql语句 mybatis实战教程mybatis in action之九mybatis 代码生成工具的使用 mybatis ...

    【Java-框架-Mybatis】(01) - 文件

    - 分页查询:通过设置RowBounds对象实现物理分页,或者使用Mybatis Plus等扩展库进行更复杂的分页。 - 复杂查询:利用Mybatis的动态SQL,可以轻松处理复杂条件的查询。 总结,Mybatis作为一款优秀的Java持久层...

    mybatis_plus

    4. 分页查询:内建的分页插件实现了物理分页和逻辑分页,支持多种数据库,且配置简单。 5. CRUD操作:提供了基本的增删改查接口,只需继承BaseMapper,即可一键生成对应的Mapper和XML文件。 三、主要功能 1. CRUD...

    实现分页查询(上)

    2. 物理分页:将数据预先分割成固定大小的页存储,每次请求时直接读取指定页的数据,适用于数据量极大且频繁访问的情况。 3. 记录数分页:先查询总记录数,然后计算出总页数,再根据页码获取数据,适用于数据量较小...

    java逻辑分页

    逻辑分页不同于物理分页,物理分页是在数据库层面进行数据切片,而逻辑分页则是在应用程序层面上实现,通常通过查询全部数据并按需显示每一页的数据。 这篇博客“java逻辑分页”可能详细介绍了如何在Java应用中实现...

    java分页例子

    1. 使用SQL语句实现分页:最直接的方法是在SQL查询语句中添加LIMIT和OFFSET关键字(MySQL),或者使用ROW_NUMBER()函数配合子查询(如Oracle、SQL Server)。例如: ```sql SELECT * FROM (SELECT ROW_NUMBER() ...

    基于SSM ( Spring + SpringMVC + Mybatis)开发的知识问答社区+源代码+文档说明

    数据的物理分页使用的是开源项目Mybatis-Page-Helper ,也是非常的轻量和易于引入。hibernate-validator的数据实体验证也使得参数的验证变得简单了起来。随着不停的实现与增加功能,引入的东西也变得丰富了起来。 #...

    MybatisPlus使用实战,MybatisPlus简介和使用示例

    这篇文章将深入探讨MybatisPlus的基本概念、核心特性以及使用示例,帮助开发者更好地理解和运用这个工具。 一、MybatisPlus简介 MybatisPlus是由百度开源的一个轻量级框架,它的主要目标是简化Mybatis的常规操作,...

    SpringbootMybatisPlusPractice

    3. **分页查询**:Mybatis Plus内置了分页插件,只需传入Page对象即可实现分页查询,支持多种数据库的分页方式。 4. **填充公共字段**:通过`@TableField(fill = FieldFill.INSERT)`或`@TableField(fill = ...

    java面试知识

    - 或使用WITH子句配合RANK()函数实现更复杂的分页逻辑。 ##### Oracle的基本数据类型 - **NUMBER**:数值类型。 - **VARCHAR2**:可变长度字符串。 - **DATE**:日期类型。 - **TIMESTAMP**:精确到毫秒的时间戳。...

    SingleMall-项目环境搭建-前后端分离-图灵杨过1

    PageHelper是MyBatis的物理分页插件,有助于提高查询性能。此外,项目还利用Swagger-UI生成API文档,方便接口的管理和测试。搜索引擎Elasticsearch用于提供高效的搜索功能,RabbitMQ处理消息队列,实现异步通信。...

Global site tag (gtag.js) - Google Analytics