MyBatis 容許你在已映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括:
* Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
* ParameterHandler (getParameterObject, setParameters)
* ResultSetHandler (handleResultSets, handleOutputParameters)
* StatementHandler (prepare, parameterize, batch, update, query)html
這裏主要使用的是對StatementHandler.prepare方法進行攔截。java
1.經過反射獲取查詢中設置的BoundSql,RowBounds,構造分頁查詢SQL(添加分頁條件)。sql
2.執行查詢前必需要還原RowBounds 爲默認值,否則第二次查詢結果不正確(或查詢不到數據-緣由如今具體不清楚)。apache
3.這裏的分頁主要是針對PreparedStatementHandler,若是是其餘的StatementHandler,能夠添加條件判斷,不執行後面的操做。session
/** * */ package org.rico.core.mybatis.plugins; import java.lang.reflect.Field; import java.sql.Connection; import java.util.Properties; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.ibatis.executor.statement.PreparedStatementHandler; import org.apache.ibatis.executor.statement.RoutingStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Mybatis 分頁插件 * @author rico * MyBatis 容許你在已映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括: * * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) * ParameterHandler (getParameterObject, setParameters) * ResultSetHandler (handleResultSets, handleOutputParameters) * StatementHandler (prepare, parameterize, batch, update, query) * * http://www.mybatis.org/mybatis-3/zh/configuration.html */ @Intercepts({@Signature( type= StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class MybatisPageQueryPlugin implements Interceptor { @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(MybatisPageQueryPlugin.class); /* (non-Javadoc) * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation) */ @Override public Object intercept(Invocation invocation) throws Throwable { // target:RoutingStatementHandler Object target = invocation.getTarget(); // delegate:PreparedStatementHandler Field delegateField = FieldUtils.getDeclaredField(RoutingStatementHandler.class, "delegate", true); PreparedStatementHandler preparedStatementHandler = (PreparedStatementHandler) delegateField.get(target); // get BoundSql field Field boundSqlField = FieldUtils.getField(PreparedStatementHandler.class, "boundSql", true); BoundSql boundSql = (BoundSql) boundSqlField.get(preparedStatementHandler); //logger.info("##boundSql={}", boundSql.getSql()); // get RowBounds field Field rowBoundsField = FieldUtils.getField(PreparedStatementHandler.class, "rowBounds", true); RowBounds rowBounds = (RowBounds) rowBoundsField.get(preparedStatementHandler); //logger.info("rowBounds.offset={}, limit={}", rowBounds.getOffset(), rowBounds.getLimit()); // sql 添加分頁 Field sqlField = FieldUtils.getDeclaredField(BoundSql.class, "sql", true); //String pagingSql = PageQuerySqlBuilder.buildMySqlPagingSql(boundSql.getSql(), rowBounds.getOffset(), rowBounds.getLimit()); String pagingSql = PageQuerySqlBuilder.buildMySqlPagingSql(boundSql.getSql(), rowBounds); sqlField.set(boundSql, pagingSql); //logger.info("##page sql = {}", boundSql.getSql()); FieldUtils.writeField(rowBounds, "offset", RowBounds.NO_ROW_OFFSET, true); FieldUtils.writeField(rowBounds, "limit", RowBounds.NO_ROW_LIMIT, true); //rowBoundsField.set(preparedStatementHandler, new RowBounds()); //rowBounds = (RowBounds) rowBoundsField.get(preparedStatementHandler); //logger.info("rowBounds.offset={}, limit={}", rowBounds.getOffset(), rowBounds.getLimit()); return invocation.proceed(); } /* (non-Javadoc) * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object) */ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } /* (non-Javadoc) * @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties) */ @Override public void setProperties(Properties properties) { } }
SQL構造工具:mybatis
/** * */ package org.rico.core.mybatis.plugins; import org.apache.ibatis.session.RowBounds; /** * MySql 分頁工具類 * @author rico * */ public class PageQuerySqlBuilder { /** * MySql 查詢添加分頁條件 * @param sql * @param rowBounds * @return */ public static String buildMySqlPagingSql(String sql, RowBounds rowBounds) { if(!checkSelectionSql(sql, rowBounds)) { return sql; } // 添加分頁條件 StringBuffer sqlBuf = new StringBuffer(sql); sqlBuf.append(" limit ").append(rowBounds.getOffset()).append(", ").append(rowBounds.getLimit()); return sqlBuf.toString(); } /** * Oracle 查詢添加分頁條件 * @param sql * @param pageIndex * @param pageSize * @return */ public static String buildOraclePagingSql(String sql, RowBounds rowBounds) { // 判斷是否查詢記錄SQL if(!checkSelectionSql(sql, rowBounds)) { return sql; } /* *SELECT T.* FROM ( * SELECT S.*, ROWNUM RN FROM ( * SELECT * FROM A * ) S WHERE ROWNUM <=#{lastIndex, jdbcType=INTEGER} *) T WHERE RN > #{pageIndex, jdbcType=INTEGER} */ int offset = rowBounds.getOffset(); int limit = rowBounds.getLimit(); int lastIndex = limit + offset; // 添加分頁條件 StringBuffer sqlBuf = new StringBuffer(); sqlBuf.append("SELECT T.* FROM ("); sqlBuf.append("SELECT S.*, ROWNUM RN FROM ("); sqlBuf.append(sql); sqlBuf.append(") S WHERE ROWNUM <=").append(lastIndex).append(")"); sqlBuf.append(" T WHERE RN > ").append(offset); return sqlBuf.toString(); } /** * 判斷是否查詢記錄SQL * @param sql * @return */ public static boolean checkSelectionSql(String sql, RowBounds rowBounds) { //TODO 沒有rowBounds參數,不添加分頁查詢條件 if(rowBounds == null) { return false; } //TODO 默認rowBounds參數,不添加分頁查詢條件 RowBounds defaultRowBounds = new RowBounds(); if(rowBounds.getLimit()==defaultRowBounds.getLimit() && rowBounds.getOffset()==defaultRowBounds.getOffset()) { return false; } //TODO select|SELECT * FROM XXX 查詢語句檢查 return true; } }