(注:以下代碼是基於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>