/*
* The MIT License (MIT)
*
* Copyright (c) 2014 abel533@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.pagehelper;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.builder.annotation.ProviderSqlSource;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Mybatis - sql工具,获取分页和count的MappedStatement,设置分页参数
*
* @author liuzh/abel533/isea533
* @since 3.3.0
* 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class SqlUtil {
private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0);
//分页的id后缀
private static final String SUFFIX_PAGE = "_PageHelper";
//count查询的id后缀
private static final String SUFFIX_COUNT = SUFFIX_PAGE + "_Count";
//第一个分页参数
private static final String PAGEPARAMETER_FIRST = "First" + SUFFIX_PAGE;
//第二个分页参数
private static final String PAGEPARAMETER_SECOND = "Second" + SUFFIX_PAGE;
private static final String PROVIDER_OBJECT = "_provider_object";
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
/**
* 反射对象,增加对低版本Mybatis的支持
*
* @param object 反射对象
* @return
*/
private static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
private SqlUtil.Parser sqlParser;
//数据库方言 - 使用枚举限制数据库类型
public enum Dialect {
mysql, mariadb, sqlite, oracle, hsqldb, postgresql
}
/**
* 构造方法
*
* @param strDialect
*/
public SqlUtil(String strDialect) {
if (strDialect == null || "".equals(strDialect)) {
throw new IllegalArgumentException("Mybatis分页插件无法获取dialect参数!");
}
try {
Dialect dialect = Dialect.valueOf(strDialect);
String sqlParserClass = this.getClass().getPackage().getName() + ".SqlParser";
try {
//使用SqlParser必须引入jsqlparser-x.x.x.jar
Class.forName("net.sf.jsqlparser.statement.select.Select");
sqlParser = (Parser) Class.forName(sqlParserClass).getConstructor(Dialect.class).newInstance(dialect);
} catch (Exception e) {
//找不到时,不用处理
}
if (sqlParser == null) {
sqlParser = SimpleParser.newParser(dialect);
}
} catch (IllegalArgumentException e) {
String dialects = null;
for (Dialect d : Dialect.values()) {
if (dialects == null) {
dialects = d.toString();
} else {
dialects += "," + d;
}
}
throw new IllegalArgumentException("Mybatis分页插件dialect参数值错误,可选值为[" + dialects + "]");
}
}
/**
* 设置分页参数
*
* @param parameterObject
* @param page
* @return
*/
public Map setPageParameter(MappedStatement ms, Object parameterObject, Page page) {
BoundSql boundSql = ms.getBoundSql(parameterObject);
return sqlParser.setPageParameter(ms, parameterObject, boundSql, page);
}
/**
* 处理count查询的MappedStatement
*
* @param ms
* @param sqlSource
* @param args
*/
public void processCountMappedStatement(MappedStatement ms, SqlSource sqlSource, Object[] args) {
args[0] = getMappedStatement(ms, sqlSource, args[1], SUFFIX_COUNT);
}
/**
* 处理分页查询的MappedStatement
*
* @param ms
* @param sqlSource
* @param page
* @param args
*/
public void processPageMappedStatement(MappedStatement ms, SqlSource sqlSource, Page page, Object[] args) {
args[0] = getMappedStatement(ms, sqlSource, args[1], SUFFIX_PAGE);
//处理入参
args[1] = setPageParameter((MappedStatement) args[0], args[1], page);
}
/**
* 处理SQL
*/
public static interface Parser {
void isSupportedSql(String sql);
String getCountSql(String sql);
String getPageSql(String sql);
Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql, Page page);
}
public static abstract class SimpleParser implements Parser {
public static Parser newParser(Dialect dialect) {
Parser parser = null;
switch (dialect) {
case mysql:
case mariadb:
case sqlite:
parser = new MysqlParser();
break;
case oracle:
parser = new OracleParser();
break;
case hsqldb:
parser = new HsqldbParser();
break;
case postgresql:
default:
parser = new PostgreSQLParser();
}
return parser;
}
public void isSupportedSql(String sql) {
if (sql.trim().toUpperCase().endsWith("FOR UPDATE")) {
throw new RuntimeException("分页插件不支持包含for update的sql");
}
}
/**
* 获取总数sql - 如果要支持其他数据库,修改这里就可以
*
* @param sql 原查询sql
* @return 返回count查询sql
*/
public String getCountSql(final String sql) {
isSupportedSql(sql);
StringBuilder stringBuilder = new StringBuilder(sql.length() + 40);
stringBuilder.append("select count(*) from (");
str