(注:如下代碼是基於ibatis3 beta4的擴展,ibatis3正式版若是實現改變,將會繼續跟進修改)javascript
iBatis3默認使用的分頁是基於遊標的分頁,而這種分頁在不一樣的數據庫上性能差別不一致,最好的辦法固然是使用相似hibernate的基於方言(Dialect)的物理分頁功能。java
iBatis3如今提供插件功能,經過插件咱們能夠編寫本身的攔截器來攔截iBatis3的主要執行方法來完成相關功能的擴展。mysql
可以攔截的的類以下:sql
- Executor
- (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
- ParameterHandler
- (getParameterObject,setParameters)
- ResultSetHandler
- (handleResultSets,handleOutputParameters)
- StatementHandler
- (prepare,parameterize,batch,update,query)
但這次咱們感興趣的是Executor.query()方法,查詢方法最終都是執行該方法。數據庫
該方法完整是:api
- Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
分頁方言的基本實現:app
以Mysql數據庫示例,即有一個方法 query(sql,offset,limit);svn
- query("select * from user",5,10);
通過咱們的攔截器攔截處理,變成性能
- query("select * from user limit 5,10",,);
1.攔截類實現:fetch
- @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 = ;
- 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) {
-
- 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, , 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>
-
- <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 > ) {
- 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插件