iBatis3基于方言(Dialect)的分页

iBatis3基于方言(Dialect)的分页

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

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

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

能够拦截的的类如下:

Java代码 复制代码 收藏代码
  1. Executor 
  2.     (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed) 
  3. ParameterHandler 
  4.     (getParameterObject,setParameters) 
  5. ResultSetHandler 
  6.     (handleResultSets,handleOutputParameters) 
  7. StatementHandler 
  8.     (prepare,parameterize,batch,update,query) 
Executor
	(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler
	(getParameterObject,setParameters)
ResultSetHandler
	(handleResultSets,handleOutputParameters)
StatementHandler
	(prepare,parameterize,batch,update,query)

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

该方法完整是:

Java代码 复制代码 收藏代码
  1. Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; 
Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

分页方言的基本实现:

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

Java代码 复制代码 收藏代码
  1. query("select * from user",5,10); 
query("select * from user",5,10);

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

Java代码 复制代码 收藏代码
  1. query("select * from user limit 5,10",0,0); 
query("select * from user limit 5,10",0,0);

1.拦截类实现:

Java代码 复制代码 收藏代码
  1. @Intercepts({@Signature
  2.         type= Executor.class
  3.         method = "query"
  4.         args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) 
  5. public class OffsetLimitInterceptor implements Interceptor{ 
  6.     static int MAPPED_STATEMENT_INDEX = 0
  7.     static int PARAMETER_INDEX = 1
  8.     static int ROWBOUNDS_INDEX = 2
  9.     static int RESULT_HANDLER_INDEX = 3
  10.      
  11.     Dialect dialect; 
  12.      
  13.     public Object intercept(Invocation invocation) throws Throwable { 
  14.         processIntercept(invocation.getArgs()); 
  15.         return invocation.proceed(); 
  16.     } 
  17.  
  18.     void processIntercept(final Object[] queryArgs) { 
  19.         //queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) 
  20.         MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX]; 
  21.         Object parameter = queryArgs[PARAMETER_INDEX]; 
  22.         final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX]; 
  23.         int offset = rowBounds.getOffset(); 
  24.         int limit = rowBounds.getLimit(); 
  25.          
  26.         if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) { 
  27.             BoundSql boundSql = ms.getBoundSql(parameter); 
  28.             String sql = boundSql.getSql().trim(); 
  29.             if (dialect.supportsLimitOffset()) { 
  30.                 sql = dialect.getLimitString(sql, offset, limit); 
  31.                 offset = RowBounds.NO_ROW_OFFSET; 
  32.             } else
  33.                 sql = dialect.getLimitString(sql, 0, limit); 
  34.             } 
  35.             limit = RowBounds.NO_ROW_LIMIT; 
  36.              
  37.             queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit); 
  38.             BoundSql newBoundSql = new BoundSql(sql, boundSql.getParameterMappings(), boundSql.getParameterObject()); 
  39.             MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql)); 
  40.             queryArgs[MAPPED_STATEMENT_INDEX] = newMs; 
  41.         } 
  42.     } 
  43.      
  44.     private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) { 
  45.         Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType()); 
  46.         builder.resource(ms.getResource()); 
  47.         builder.fetchSize(ms.getFetchSize()); 
  48.         builder.statementType(ms.getStatementType()); 
  49.         builder.keyGenerator(ms.getKeyGenerator()); 
  50.         builder.keyProperty(ms.getKeyProperty()); 
  51.         builder.timeout(ms.getTimeout()); 
  52.         builder.parameterMap(ms.getParameterMap()); 
  53.         builder.resultMaps(ms.getResultMaps()); 
  54.         builder.cache(ms.getCache()); 
  55.         MappedStatement newMs = builder.build(); 
  56.         return newMs; 
  57.     } 
  58.  
  59.     public Object plugin(Object target) { 
  60.         return Plugin.wrap(target, this); 
  61.     } 
  62.  
  63.     public void setProperties(Properties properties) { 
  64.         String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass"); 
  65.         try
  66.             dialect = (Dialect)Class.forName(dialectClass).newInstance(); 
  67.         } catch (Exception e) { 
  68.             throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e); 
  69.         }  
  70.         System.out.println(OffsetLimitInterceptor.class.getSimpleName()+".dialect="+dialectClass); 
  71.     } 
  72.      
  73.     public static class BoundSqlSqlSource implements SqlSource { 
  74.         BoundSql boundSql; 
  75.         public BoundSqlSqlSource(BoundSql boundSql) { 
  76.             this.boundSql = boundSql; 
  77.         } 
  78.         public BoundSql getBoundSql(Object parameterObject) { 
  79.             return boundSql; 
  80.         } 
  81.     } 
  82.      
@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配置文件内容:

Xml代码 复制代码 收藏代码
  1. <configuration> 
  2.     <plugins> 
  3.         <!-- 指定数据库分页方言Dialect, 其它方言:OracleDialect,SQLServerDialect,SybaseDialect,DB2Dialect,PostgreSQLDialect,MySQLDialect,DerbyDialect--> 
  4.         <plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor"> 
  5.             <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/> 
  6.         </plugin> 
  7.     </plugins> 
  8.  
  9.     <environments default="development"> 
  10.         <environment id="development"> 
  11.             <transactionManager type="JDBC" /> 
  12.             <dataSource type="POOLED"> 
  13.                 <property name="driver" value="com.mysql.jdbc.Driver" /> 
  14.                 <property name="url" value="jdbc:mysql://localhost:3306/test" /> 
  15.                 <property name="username" value="root" /> 
  16.                 <property name="password" value="123456" /> 
  17.             </dataSource> 
  18.         </environment> 
  19.     </environments> 
  20.  
  21.     <mappers> 
  22.         <mapper resource="com/company/project/model/mapper/BlogMapper.xml" /> 
  23.     </mappers> 
  24. </configuration> 

3.MysqlDialect实现

Java代码 复制代码 收藏代码
  1. public class MySQLDialect extends Dialect{ 
  2.  
  3.     public boolean supportsLimitOffset(){ 
  4.         return true
  5.     } 
  6.      
  7.     public boolean supportsLimit() {    
  8.         return true;    
  9.     }   
  10.      
  11.     public String getLimitString(String sql, int offset, int limit) { 
  12.             return getLimitString(sql,offset,String.valueOf(offset),limit,String.valueOf(limit)); 
  13.     } 
  14.      
  15.     public String getLimitString(String sql, int offset,String offsetPlaceholder, int limit, String limitPlaceholder) { 
  16.         if (offset > 0) {    
  17.             return sql + " limit "+offsetPlaceholder+","+limitPlaceholder;  
  18.         } else {    
  19.             return sql + " limit "+limitPlaceholder; 
  20.         }   
  21.     }    
  22.    
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插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值