簡單的MyBatis分頁插件

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;
	}
	
}
相關文章
相關標籤/搜索