iBatis3基於方言(Dialect)的分頁

(注:以下代碼是基於ibatis3 beta4的擴展,ibatis3正式版如果實現改變,將會繼續跟進修改)

iBatis3默認使用的分頁是基於遊標的分頁,而這種分頁在不同的數據庫上性能差異不一致,最好的辦法當然是使用類似hibernate的基於方言(Dialect)的物理分頁功能。

iBatis3現在提供插件功能,通過插件我們可以編寫自己的攔截器來攔截iBatis3的主要執行方法來完成相關功能的擴展。

能夠攔截的的類如下:

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

但此次我們感興趣的是Executor.query()方法,查詢方法最終都是執行該方法。

該方法完整是:

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

分頁方言的基本實現:

以Mysql數據庫示例,即有一個方法 query(sql,offset,limit);

Java代碼 收藏代碼
query("select * from user",5,10);

經過我們的攔截器攔截處理,變成

Java代碼 收藏代碼
query("select * from user limit 5,10",0,0);

1.攔截類實現:

Java代碼 收藏代碼
@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代碼 收藏代碼
<configuration>
<plugins>

3.MysqlDialect實現 Java代碼 收藏代碼 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插件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章