本文是原创文章,转载请注明出处:http://my.oschina.net/fqt/blog/52431
一直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率在多数情况下不及特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分利用了特定数据库的分页机制,效率相对较高。
网上已有《使ibatis支持hibernate式的物理分页》等类似的文章以继承SqlExecutor的方式实现了物理分页,但是侵入性非常强,还得实现数据库方言,方法非常复杂。同时SqlExecutor不是接口,对它的方法继承也不能保证版本稳定。本文中将介绍的方式是实现queryWithSqlHandler方法,在查询前将通过SqlHandler接口把sql传给调用者,再用处理后的sql进行最终查询,从而实现物理分页等功能:
//用queryWithSqlHandler方法实现物理分页的例子:
public Page queryPage(String statementId, param, final Page page){
final int pageNum = page.getPageNum();
final int pageSize = page.getPageSize();
List list = queryWithSqlHandler(statementId, param, new SqlHandler() {
@Override
public String handle(String sql, Object[] params) throws SQLException {
//查询总记录数
int total = getJdbcTemplate().queryForInt("select count(1) as RECORDS from (" + sql + ")", params);
page.setTotal(total);
//返回经过分页包装后的Sql
return "select * from (select row_.*, rownum row_num_ from ("+sql+") row_ where rownum<="+ pageNum*pageSize +") where row_num_ > "+ (pageNum-1)*pageSize;
}
});
page.setRows(list);
return page;
}
我们来看queryWithSqlHandler方法的实现:
/**
* 提供查询前对sql处理的功能
*
* @param statementId
* @param param
* @param sqlHandler sql处理器
* @return
*/
private List queryWithSqlHandler(final String statementId, final Object param, final SqlHandler sqlHandler) {
final SqlMapClientImpl smc = getSqlMapClient();
if (sqlHandler != null) {
final MappedStatement mappedStatement = smc.getMappedStatement(statementId);
final Sql dySql = mappedStatement.getSql();
if (Proxy.isProxyClass(dySql.getClass())) {
log.debug("该Sql对象已经是代理对象,设置新的sql处理器。");
((SqlProxyHandler) Proxy.getInvocationHandler(dySql)).setSqlHandler(sqlHandler);
} else {
log.debug("创建Sql的代理对象!");
final SqlProxyHandler sqlProxyHandler = new SqlProxyHandler(dySql, sqlHandler);
final Class sqlClass = dySql.getClass();
final Sql proxy = (Sql) Proxy.newProxyInstance(sqlClass.getClassLoader(), sqlClass.getInterfaces(), sqlProxyHandler);
mappedStatement.setSql(proxy);
}
}
try {
return smc.queryForList(statementId, param);
} catch (SQLException ex) {
throw new RuntimeException("查询失败", ex);
}
}
private static final class SqlProxyHandler implements InvocationHandler {
private final Sql sql;
private final ThreadLocal<SqlHandler> sqlHandler = new ThreadLocal();
public SqlProxyHandler(Sql sql, SqlHandler handler) {
this.sql = sql;
setSqlHandler(handler);
}
public Sql getSql() {
return sql;
}
public void setSqlHandler(SqlHandler handler) {
this.sqlHandler.set(handler);
}
public SqlHandler getSqlHandler() {
return sqlHandler.get();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(getSql(), args);
if ("getSql".equals(method.getName()) && getSqlHandler() != null) {
log.debug("原SQL: " + result);
final StatementScope statementScope = (StatementScope) args[0];
final Object[] params = statementScope.getParameterMap().getParameterObjectValues(statementScope, args[1]);
result = getSqlHandler().handle((String) result, params);
log.debug("处理后: " + result);
setSqlHandler(null);//执行完成后清除线程局部变量,下次调用需要设置新值,否则不拦截getSql方法
}
return result;
}
}
interface SqlHandler {
/**
* 处理sql语句
*
* @param sql ibatis生成的sql语句,其中参数用?号占位
* @param params sql对应的参数
* @return
* @throws Throwable
*/
String handle(String sql, Object[] params) throws Throwable;
}
来源:oschina
链接:https://my.oschina.net/u/139242/blog/52431