MyBatis分頁插件實現


日常開發中,MyBatis已經成為數據持久層實現的重要角色,以下就是一個使用MyBatis開發的一個分頁插件的實現。關於Mybatis的插件概念可以查看MyBatis官網

MyBatis插件源碼包結構

查看官網教程可以得知,MyBatis允許客戶對以下類的方法進行攔截。

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

使用插件是非常簡單的,只需實現 Interceptor 接口,並指定想要攔截的方法簽名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

對官網教程的解讀:

  1. 用戶自定義攔截器類需要實現Interceptor接口。
  2. 指定攔截器需要攔截的方法。ExamplePlugin是對Executor的update方法進行攔截。查看Executor源碼如下:
    int update(MappedStatement ms, Object parameter) throws SQLException;
  3. 配置文件

以下為分頁插件的實現,是對Executor

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
```方法的攔截。

1.定義攔截器類,實現<code>Interceptor</code>接口,指定攔截的方法。
```Java
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class PageInterceptor 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) {
        MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
        Object parameter = queryArgs[PARAMETER_INDEX];
        // 獲取分頁信息RowBounds
        final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX];
        int pageCurrent = rowBounds.getOffset();
        int pageSize = rowBounds.getLimit();

        // 獲取NAMESPACE和方法
        String[] nameSpaceId = ms.getId().split("\\.");
        if (!ArrayUtils.isEmpty(nameSpaceId) && nameSpaceId[nameSpaceId.length - 1].startsWith("query")) {
            if (dialect.supportsLimit() && (pageCurrent != RowBounds.NO_ROW_OFFSET || pageSize != RowBounds.NO_ROW_LIMIT)) {
                BoundSql boundSql = ms.getBoundSql(parameter);
                String sql = boundSql.getSql().trim();
                if (dialect.supportsLimitOffset()) {
                    sql = dialect.getPageSql(sql, pageCurrent, pageSize);
                    pageCurrent = RowBounds.NO_ROW_OFFSET;
                } else {
                    sql = dialect.getPageSql(sql, 0, pageSize);
                }
                pageSize = RowBounds.NO_ROW_LIMIT;

                queryArgs[ROWBOUNDS_INDEX] = new RowBounds(pageCurrent, pageSize);
                BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
                MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
                
                //需要將metaParameters賦值過去..  
                MetaObject countBsObject = SystemMetaObject.forObject(newBoundSql);  
                MetaObject boundSqlObject = SystemMetaObject.forObject(boundSql);  
                countBsObject.setValue("metaParameters",boundSqlObject.getValue("metaParameters"));  
                
                queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
            }
        }
    }

    /** @see MapperBuilderAssistant */
    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.getKeyProperties() == null ? null : ms.getKeyProperties()[0]);

        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());

        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

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

    public void setProperties(Properties properties) {
        try { 
            String dialectClassValue  =properties.getProperty("dialectClass");
            if("com.page.dialect.MySqlDialect".equals(dialectClassValue)){
                dialect = (Dialect) Class.forName(MySqlDialect.class.getCanonicalName()).newInstance();
            }else{
                dialect = (Dialect) Class.forName(OracleDialect.class.getCanonicalName()).newInstance();
            }
        } catch (Exception e) {
            throw new RuntimeException("cannot create dialect instance by dialectClass : " + OracleDialect.class.getCanonicalName(), e);
        }
    }

    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}

/**
 * 類似HIBERNATE的Dialect,但只精簡出分頁部分
 */
public class Dialect {

    /**
     * 將SQL變成分頁SQL語句</br> 
     * 拆分原始SQL,拼裝需要的分頁SQL.
     */
    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        throw new UnsupportedOperationException("paged queries not supported");
    }

    public boolean supportsLimit() {
        return false;
    }

    public boolean supportsLimitOffset() {
        return supportsLimit();
    }
}

public class MySqlDialect extends Dialect {

    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        // 原始SQL
        String orgSql = sql.trim();
        // 重置當前頁
        pageCurrent = pageCurrent - 1;
        //起始數據行
        int offset=pageCurrent*pageSize;
        // 最終SQL
        StringBuffer finaleSql = new StringBuffer();
        
        finaleSql.append("select * from ( ");
        

        // 追加原始SQL
        finaleSql.append(orgSql);

        //用limit分頁
        finaleSql.append(" limit "+String.valueOf(offset)+","+String.valueOf(pageSize));
         
        
        finaleSql.append("  ) as tabletotal");
        
    
        return finaleSql.toString();
    }

    public boolean supportsLimit() {
        return true;
    }

    public boolean supportsLimitOffset() {
        return true;
    }
}


public class OracleDialect extends Dialect {

    public String getPageSql(String sql, int pageCurrent, int pageSize) {
        // 原始SQL
        String orgSql = sql.trim();
        // 重置當前頁
        pageCurrent = pageCurrent - 1;

        // 最終SQL
        StringBuffer finaleSql = new StringBuffer();
        // 如果當前頁>0
        finaleSql.append("select * from (select * from ( select row_.*, rownum rownum_ from ( ");

        // 追加原始SQL
        finaleSql.append(orgSql);

        // 如果當前頁>0
        if (pageCurrent > 0) {
            finaleSql.append(" ) row_ )) where rownum_ <= " + ((pageCurrent * pageSize) + "+" + pageSize) + " and rownum_ > "
                    + String.valueOf(pageCurrent * pageSize));
        } else {// 如果當前頁<=0
            finaleSql.append(" ) row_ )) where rownum_ <= " + String.valueOf(pageSize));
        }

        return finaleSql.toString();
    }

    public boolean supportsLimit() {
        return true;
    }

    public boolean supportsLimitOffset() {
        return true;
    }
}



public class SystemMetaObject {
    public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY,
            DEFAULT_OBJECT_WRAPPER_FACTORY);

    private SystemMetaObject() {
        // Prevent Instantiation of Static Class
    }

    private static class NullObject {
    }

    public static MetaObject forObject(Object object) {
        return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
    }
}

<configuration>
    <properties>
        <property name="dialect" value="oracle" />
    </properties>
    <settings>
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="jdbcTypeForNull" value="VARCHAR" />
    </settings>
    <plugins>
        <plugin interceptor="com.page.interceptor.PageInterceptor">
            <property name="dialectClass" value="com..page.dialect.OracleDialect" />
        </plugin>
    </plugins>
</configuration>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM