/*
* Copyright 2013 The Solmix Project
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.gnu.org/licenses/
* or see the FSF site: http://www.fsf.org.
*/
package org.solmix.mybatis;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
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.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.solmix.sql.SQLDriver;
/**
*
* @author solmix.f@gmail.com
* @version $Id$ 2014年6月15日
*/
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class PageInterceptor implements Interceptor
{
/**
* {@inheritDoc}
*
* @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement statement = (MappedStatement) args[0];
Object parameter = args[1];
if (parameter instanceof MybatisParameter) {
MybatisParameter p = (MybatisParameter) parameter;
if (p.isCanPage()) {
BoundSql boundSql = statement.getBoundSql(p.getCriteria());
String originalSql = boundSql.getSql().trim();
SQLDriver sqlDriver = p.getSqlDriver();
int total= p.getRequest().getContext().getTotalRow();
if(total<=0){
String countSql = sqlDriver.getRowCountQueryString(originalSql);
Connection connection = statement.getConfiguration().getEnvironment().getDataSource().getConnection();
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = copyFromBoundSql(statement, boundSql,
countSql, p.getCriteria());
DefaultParameterHandler parameterHandler = new DefaultParameterHandler(
statement, p.getCriteria(), countBS);
parameterHandler.setParameters(countStmt);
ResultSet rs = countStmt.executeQuery();
if (rs.next()) {
total = rs.getInt(1);
}
rs.close();
countStmt.close();
connection.close();
}
p.getResponse().setTotalRows(total);
int end = p.getRequest().getContext().getEndRow();
int start = p.getRequest().getContext().getStartRow();
int batch = p.getRequest().getContext().getBatchSize();
if (end != -1 && end - start > batch) {
batch = end - start;
p.getRequest().getContext().setBatchSize(batch);
}
String limitQuery = sqlDriver.limitQuery(originalSql, start,
batch, null);
BoundSql newBoundSql = copyFromBoundSql(statement, boundSql,
limitQuery.toString(), p.getCriteria());
MappedStatement newMs = copyFromMappedStatement(statement,
new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0] = newMs;
invocation.getArgs()[1] = p.getCriteria();
}
}
return invocation.proceed();
}
public class BoundSqlSqlSource implements SqlSource
{
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql)
{
this.boundSql = boundSql;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql,
String sql, Object parameterObject) {
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql,
boundSql.getParameterMappings(), parameterObject);
for (ParameterMapping mapping : boundSql.getParameterMappings()) {
String prop = mapping.getProperty();
if (boundSql.hasAdditionalParameter(prop)) {
newBoundSql.setAdditionalParameter(prop,
boundSql.getAdditionalParameter(prop));
}
}
return newBoundSql;
}
private MappedStatement copyFromMappedStatement(MappedStatement ms,
SqlSource newSqlSource) {
Builder builder = new Builder(ms.getConfiguration(), ms.getId(),
newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
String[] keys = ms.getKeyProperties();
if (keys != null && keys.length > 0) {
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key);
sb.append(",");
}
String keyProperty = sb.deleteCharAt(sb.length()).toString();
builder.keyProperty(keyProperty);
}
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();
}
/**
* {@inheritDoc}
*
* @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* {@inheritDoc}
*
* @see org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
*/
@Override
public void setProperties(Properties properties) {
//nothing todo
}
}