`
badqiu
  • 浏览: 673976 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
社区版块
存档分类
最新评论

iBatis3基于方言(Dialect)的分页

阅读更多

(注:以下代码是基于ibatis3 beta4的扩展,ibatis3正式版如果实现改变,将会继续跟进修改)

 

 

iBatis3默认使用的分页是基于游标的分页,而这种分页在不同的数据库上性能差异不一致,最好的办法当然是使用类似hibernate的基于方言(Dialect)的物理分页功能。

iBatis3现在提供插件功能,通过插件我们可以编写自己的拦截器来拦截iBatis3的主要执行方法来完成相关功能的扩展。

 

 

 

能够拦截的的类如下:

 

Executor
	(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler
	(getParameterObject,setParameters)
ResultSetHandler
	(handleResultSets,handleOutputParameters)
StatementHandler
	(prepare,parameterize,batch,update,query)

 

 但此次我们感兴趣的是Executor.query()方法,查询方法最终都是执行该方法。

该方法完整是:

 

Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

 

 

分页方言的基本实现:

以Mysql数据库示例,即有一个方法 query(sql,offset,limit);

 

 

query("select * from user",5,10);
 

 

经过我们的拦截器拦截处理,变成

 

query("select * from user limit 5,10",0,0);
 

 

1.拦截类实现:

 

@Intercepts({@Signature(
		type= Executor.class,
		method = "query",
		args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class OffsetLimitInterceptor 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) {
		//queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
		MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
		Object parameter = queryArgs[PARAMETER_INDEX];
		final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];
		int offset = rowBounds.getOffset();
		int limit = rowBounds.getLimit();
		
		if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
			BoundSql boundSql = ms.getBoundSql(parameter);
			String sql = boundSql.getSql().trim();
			if (dialect.supportsLimitOffset()) {
				sql = dialect.getLimitString(sql, offset, limit);
				offset = RowBounds.NO_ROW_OFFSET;
			} else {
				sql = dialect.getLimitString(sql, 0, limit);
			}
			limit = RowBounds.NO_ROW_LIMIT;
			
			queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);
			BoundSql newBoundSql = new BoundSql(sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
			MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
			queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
		}
	}
	
	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.getKeyProperty());
		builder.timeout(ms.getTimeout());
		builder.parameterMap(ms.getParameterMap());
		builder.resultMaps(ms.getResultMaps());
		builder.cache(ms.getCache());
		MappedStatement newMs = builder.build();
		return newMs;
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
		String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass");
		try {
			dialect = (Dialect)Class.forName(dialectClass).newInstance();
		} catch (Exception e) {
			throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e);
		} 
		System.out.println(OffsetLimitInterceptor.class.getSimpleName()+".dialect="+dialectClass);
	}
	
	public static class BoundSqlSqlSource implements SqlSource {
		BoundSql boundSql;
		public BoundSqlSqlSource(BoundSql boundSql) {
			this.boundSql = boundSql;
		}
		public BoundSql getBoundSql(Object parameterObject) {
			return boundSql;
		}
	}
	
}
 

 

2.ibatis3配置文件内容:

 

<configuration>
	<plugins>
		<!-- 指定数据库分页方言Dialect, 其它方言:OracleDialect,SQLServerDialect,SybaseDialect,DB2Dialect,PostgreSQLDialect,MySQLDialect,DerbyDialect-->
		<plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor">
			<property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/>
		</plugin>
	</plugins>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/test" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>

	<mappers>
		<mapper resource="com/company/project/model/mapper/BlogMapper.xml" />
	</mappers>
</configuration>
 

3.MysqlDialect实现

 

public class MySQLDialect extends Dialect{

	public boolean supportsLimitOffset(){
		return true;
	}
	
	public boolean supportsLimit() {   
		return true;   
	}  
    
	public String getLimitString(String sql, int offset, int limit) {
    		return getLimitString(sql,offset,String.valueOf(offset),limit,String.valueOf(limit));
	}
    
	public String getLimitString(String sql, int offset,String offsetPlaceholder, int limit, String limitPlaceholder) {
		if (offset > 0) {   
			return sql + " limit "+offsetPlaceholder+","+limitPlaceholder; 
		} else {   
		    return sql + " limit "+limitPlaceholder;
		}  
	}   
  
}

 

其它数据库的Dialect实现请查看:

http://rapid-framework.googlecode.com/svn/trunk/rapid-framework/src/rapid_framework_common/cn/org/rapid_framework/jdbc/dialect/

 

 

4.分页使用

直接调用SqlSession.selectList(statement, parameter, new RowBounds(offset,limit))即可使用物理分页

 

 

5.存在的问题

现不是使用占位符的方式(即不是“limit ?,?”而是使用“limit 8,20”)使用分页

 

完整代码可以查看即将发布的rapid-framework 3.0的ibatis3插件

 

 

 

 

 

18
3
分享到:
评论
11 楼 在世界的中心呼喚愛 2015-12-07  
好东西,我就是参考这个!!!
10 楼 王星游 2011-09-01  
谢谢,很有用。我用mybatis generator生成了大量pojo,example对象和xml文件,在楼主这种拦截器的方式下,都不用再更改了,要不然我还得去手动去每一个xml里自己添加limit条件。

不过在DAO层我也不能在简单的这样写了:
List<User> userList=userMapper.selectByExample(new UserExample());

只能采用mybatis-spring-3.0的线程安全的SqlSessionTemplate:
sqlSessionTemplate.selectList("com.watchzerg.persistence.UserMapper.selectByExample", new UserExample(), new RowBounds(0,10));
9 楼 zhouhe 2011-07-04  
Cause: java.sql.SQLException: ORA-01008: 并非所有变量都已绑定。

我试了一下,报错了,不知道我的代码有啥问题。

如果在Eclipse中Debug,把SQL中的分页参数“问号”换成数字,就好使了。

我漏掉什么步骤了?

指点一下吧。
8 楼 zhouhe 2011-07-04  
Cause: java.sql.SQLException: ORA-01008: 并非所有变量都已绑定。
7 楼 zhouhe 2011-07-04  
我试了一下,报错了,不知道我的代码有啥问题。
6 楼 liuwei_blog 2011-03-02  
赞 
5 楼 badqiu 2010-04-30  
不一定吧, 你可以写两个ibatis configation.xml吧,因为如果是不同数据库的,肯定也存在两个数据源的.
4 楼 cameron8 2010-04-26  
并且重新写个xml文件也没有用,这种做法只能用单一的数据库.
3 楼 cameron8 2010-04-26  
<plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor">  
            <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/>  


不能同时支持两种数据库Dialect,
因为dialectClass属性加载的时候会复盖
2 楼 pochonlee 2009-10-20  
3.0顶一个!期待更多。
1 楼 超级潜水员 2009-10-19  
支持lz的3.0喔!!!

相关推荐

    ibatis 之分页

    本文将深入探讨Ibatis实现分页的相关知识点,并基于提供的标签“源码”和“工具”,分享如何在实际项目中运用Ibatis进行分页处理。 首先,了解Ibatis的基本概念。Ibatis是由Apache基金会维护的一个开源项目,它是一...

    基于ibatis的分页

    本项目基于ibatis框架实现了分页功能,覆盖了从底层数据库操作到页面展示的完整流程,包括DAO层、Service层、Action层以及JSP页面的展示。 首先,我们来了解一下什么是ibatis。Ibatis是一个优秀的持久层框架,它...

    ibatis调用oracle存储过程分页

    ibatis调用oracle存储过程分页

    ibatis_likehbm高效分页组件

    ibatis_likehbm高效分页组件ibatis_likehbm高效分页组件ibatis_likehbm高效分页组件ibatis_likehbm高效分页组件ibatis_likehbm高效分页组件ibatis_likehbm高效分页组件 ibatis_likehbm高效分页组件 ibatis_likehbm...

    修改ibatis源代码实现物理分页

    综上所述,修改Ibatis源代码实现物理分页涉及的关键技术包括动态SQL、自定义插件、数据库方言处理以及性能优化。通过这样的改造,我们可以充分利用数据库的分页功能,提高查询效率,降低内存占用,从而提升整个系统...

    Ibatis 2.3.4 数据库无关分页

    总结一下,Ibatis 2.3.4的数据库无关分页特性提供了一种跨数据库的分页解决方案,通过动态SQL和参数传递,使得分页逻辑独立于具体的数据库方言。这种方式提高了代码的可复用性和可维护性,也降低了系统迁移的成本。...

    ibatis分页

    3. 分析iBATIS源码,理解执行器如何处理分页逻辑。 4. 使用工具或插件辅助iBATIS的分页配置。 5. 如何在实际项目中优化分页性能,比如使用缓存,避免全表扫描等。 对于初学者,可以先从学习iBATIS的基础用法开始,...

    ibatis物理分页jar

    ibatis 物理分页jar ,与官方ibatis不冲突,可直接使用。

    iBatis分页(基于Struts2和Freemarker)

    本知识点将深入探讨如何在Struts2框架中结合iBatis实现基于Freemarker模板的分页功能。 首先,我们需要理解iBatis,它是一个轻量级的Java持久层框架,它提供了一个SQL映射框架,允许开发者将SQL语句与Java代码分离...

    对IBatis分页的改进,使ibatis支持hibernate式的物理分页

    公司的大部分项目都开始使用IBatis作为O/R Mapping了,但是在使用的过程中也发现了很多不方便和存在争议的地方,其中一个不方便的地方就是分页,目前的处理方式都是在sqlMap中写针对特定数据库的物理分页Sql语句,对于...

    struts2+spring+ibatis+oracle+分页搜索+上传附件实例

    3. **iBatis**:iBatis是一个持久层框架,它将SQL与Java代码分离,提高了可维护性和可扩展性。在这个实例中,iBatis作为数据访问层,负责与Oracle数据库交互,执行分页查询和插入、更新、删除操作。它通过XML或注解...

    ibatis实现分页技术

    Ibatis实现分页技术主要依赖于SQL语句中的分页关键字和动态参数,通过在Mapper接口、Controller层和服务层合理封装,可以实现灵活的分页功能。如果配合使用分页插件,如PageHelper,将使分页操作更加便捷。在实际...

    Ibatis SQLServerDialect 2008 分页

    Ibatis SQLServerDialect 2008 分页 可实现SQLServerDialect 分页 支持ibatis3

    Ibatis.net 分页

    3. **使用Ibatis.NET提供的扩展分页库**:有些社区开发了针对Ibatis.NET的扩展库,如MyBatis.Impl.Pager,它提供了一个方便的分页助手类,可以简化分页查询的实现。你需要在项目中引用这个库,然后在业务逻辑层调用...

    ibatis分页功能

    标题"ibatis分页功能"指的就是如何在iBATIS框架中实现数据库查询的分页效果。分页不仅提高了用户体验,还能减少不必要的数据库负载。 描述中提到,分页功能是通过`page.tld`标签实现的。`tld`文件是JSP Tag Library...

    Ibatis3手册 Ibatis3参考手册

    本篇文章基于“Ibatis3手册 Ibatis3参考手册”的标题及描述,深入解析Ibatis3的核心概念、架构特点以及如何进行实际操作,旨在帮助读者全面理解Ibatis3的工作原理与应用场景。 ### 一、Ibatis3简介 Ibatis3是一款...

    ssi_struts2_spring_ibatis 增删改查分页

    标题中的"ssi_struts2_spring_ibatis"指的是一个基于Java技术栈的Web应用程序开发框架组合,主要包括Struts2、Spring和iBatis。这三个组件是企业级Java应用中的常见选择,它们各自承担着不同的职责。 1. **Struts2*...

    spring3.0.3+ibatis2.3.4.7分页

    3. **动态SQL**:在iBATIS的Mapper XML文件中,编写包含分页逻辑的SQL语句,使用`&lt;if&gt;`、`&lt;choose&gt;`等标签来动态拼接LIMIT和OFFSET子句,实现分页查询。 4. **Mapper接口**:在DAO层,定义一个返回分页结果的方法,...

    Struts2-spring-ibatis 整合及存储过程分页

    7. **实现存储过程分页**:在iBatis中,可以调用存储过程来实现分页查询。在Mapper接口中定义一个方法,如`selectByPage`,并编写对应的XML映射文件。存储过程通常会接受两个参数:开始位置和页大小,返回一个结果集...

    spring+ibatis+oracle分页缓存源码

    总的来说,"spring+ibatis+oracle分页缓存源码"项目展示了如何在Spring管理的环境中,利用iBatis和Oracle数据库实现高效的数据分页和缓存策略。通过理解和实践这些技术,开发者可以构建出更加健壮、响应快速的Web...

Global site tag (gtag.js) - Google Analytics