Mybatis自動分頁外掛
阿新 • • 發佈:2019-02-15
自己實現了一個比較簡單的Mybatis分頁外掛。在講解如何實現分頁外掛之前,我們先簡單介紹一下Mybatis中的一些重要的物件。我們通過對映器Mapper對資料庫進行增刪改操作時,Mapper執行的過程是通過Executor、StatementHandler、ParameterHandler和ResultHandler來完成對資料庫的操作和返回結果的。
- Executor代表執行器,由它來排程StatementHandler、ParameterHandler、ResultHandler等來執行對應的SQL。
- StatementHandler的作用是使用資料庫的Statement(PreparedStatement)執行操作,是上面提到的四個物件的核心。
- ParameterHandler用於SQL對引數的處理。
- ResultHandler是進行最後資料集(ResultSet)的封裝返回處理的。
要編寫Mybatis外掛,我們就必須要實現Interceptor介面,下面先來看看這個接口裡面的方法:
public interface Interceptor {
Object intercept(Invocation var1) throws Throwable;
Object plugin(Object var1);
void setProperties(Properties var1);
}
- intercept方法是外掛的核心方法,它有個Invocation型別的引數,通過這個引數可以反射排程原來物件的方法。
- plugin方法的作用是給被攔截的物件生成一個代理物件並返回。
- setProperties方法允許在plugin元素中配置所需引數。
攔截器簽名
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
這裡我要攔截的是Executor的query方法,先判斷有沒有PageParam型別的分頁引數,如果有的話先查詢符合條件的資料條數count,再獲取具體的資料list,將count和list封裝在Page型別的物件裡面返回。
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class EasyPage implements Interceptor {
Logger logger = LoggerFactory.getLogger(EasyPage.class);
private static int MAPPED_STATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
public Object intercept(Invocation invocation) throws Throwable {
Object[] queryArgs = invocation.getArgs();
MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
Object parameter = queryArgs[PARAMETER_INDEX];
PageParam page = new PageParam();
String pageKey = "";// 分頁引數字首
if (parameter instanceof PageParam) {// 只有分頁引數一個引數
page = (PageParam) parameter;
} else if (parameter instanceof PageParam || parameter instanceof HashMap) {// 2個及以上引數
HashMap<String, Object> parameterMap = (HashMap<String, Object>) parameter;
for (String key : parameterMap.keySet()) {
if (parameterMap.get(key) instanceof PageParam) {
page = (PageParam) parameterMap.get(key);
pageKey = key + ".";
break;
}
}
}
// 判斷是否需要分頁,當引數不是預設值的時候就進行分頁
if (page != null && page.getIndex() != 0 && page.getRows() != Integer.MAX_VALUE) {
int index = page.getIndex();
int rows = page.getRows();
BoundSql boundSql = ms.getBoundSql(parameter);
int total = this.getCount(ms, parameter, boundSql);
List list = Collections.EMPTY_LIST;
if (total > 0) {
Dialect dialect = new Dialect();
BoundSql newBoundSql = dialect.getBoungSQL(ms, boundSql, (index - 1) * rows, pageKey);
MappedStatement newMs = copyFromMappedStatement(ms, new MySqlSource(newBoundSql));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
list = (List) invocation.proceed();
}
return new Page(list, index, rows, total);
}
return invocation.proceed();
}
/**
* 獲取資料總條數
* @param mappedStatement
* @param parameter
* @param boundSql
* @return
* @throws SQLException
*/
public int getCount(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) throws SQLException {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("select count(1) from (");
sqlBuilder.append(clearOrderBy(boundSql.getSql())).append(") tmp");
Connection connection;
PreparedStatement countStmt = null;
ResultSet rs = null;
int count = 0;
try {
connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
countStmt = connection.prepareStatement(sqlBuilder.toString());
DefaultParameterHandler handler = new DefaultParameterHandler(mappedStatement, parameter, boundSql);
handler.setParameters(countStmt);
rs = countStmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
logger.debug("==> Preparing: {}", sqlBuilder.toString());
logger.debug("<== Total: {}", count);
} finally {
try {
if (rs != null) {
rs.close();
}
} finally {
if (countStmt != null) {
countStmt.close();
}
}
}
return count;
}
/**
* 根據資料庫型別設定引數,不需要在配置中設定資料庫型別,通過DatabaseMetaData物件可以取到資料庫名稱
* @param ms
* @param boundSql
* @param offset
* @param pageKey
* @return
* @throws SQLException
*/
public BoundSql getBoungSQL(MappedStatement ms, BoundSql boundSql, int offset, String pageKey) throws SQLException {
DatabaseMetaData dbmd = ms.getConfiguration().getEnvironment().getDataSource().getConnection().getMetaData();
String dbType = dbmd.getDatabaseProductName();
String sql = boundSql.getSql();
if (dbType != null) {
switch (dbType) {
case "MySQL":
sql = MysqlDialect.getLimitString(boundSql.getSql(), offset);
break;
case "Oracle":
sql = OracleDialect.getLimitString(boundSql.getSql(), offset);
break;
default:
throw new IllegalArgumentException("Not supported dialect:" + dbType);
}
}
// copy a new list, if use "list=boundSql.getParameterMappings()" will throws UnsupportedOperationException
List<ParameterMapping> list = new ArrayList<ParameterMapping>(boundSql.getParameterMappings());
if (offset > 0) {
list.add(new ParameterMapping.Builder(ms.getConfiguration(), pageKey + "offset", Integer.class).build());
list.add(new ParameterMapping.Builder(ms.getConfiguration(), pageKey + "rows", Integer.class).build());
} else {
list.add(new ParameterMapping.Builder(ms.getConfiguration(), pageKey + "rows", Integer.class).build());
}
BoundSql newboundSql = new BoundSql(ms.getConfiguration(), sql, list, boundSql.getParameterObject());
return newboundSql;
}