com.dao.genericdao.mybatis.plugins.page.support.SqlUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.dao.genericdao.mybatis.plugins.page.support.SqlUtil.java

Source

/*
 * 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.dao.genericdao.mybatis.plugins.page.support;

import com.dao.genericdao.mybatis.plugins.page.MybatisPageImpl;
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.plugin.Invocation;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
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 org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Method;
import java.util.*;

/**
 * Mybatis - sql?countMappedStatement?
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SqlUtil {
    private static final ThreadLocal<PageHelper> LOCAL_PAGE = new ThreadLocal<PageHelper>();
    //RowBounds?offsetPageNum - ?
    private boolean offsetAsPageNum = false;
    //RowBounds?count - ?
    private boolean rowBoundsWithCount = false;
    //truepagesize0RowBoundslimit=0?
    private boolean pageSizeZero = false;
    //??
    private boolean reasonable = false;
    //params?
    private static Map<String, String> PARAMS = new HashMap<String, String>(5);
    //request?
    private static Boolean hasRequest;
    private static Class<?> requestClass;
    private static Method getParameterMap;

    static {
        try {
            requestClass = Class.forName("javax.servlet.ServletRequest");
            getParameterMap = requestClass.getMethod("getParameterMap", new Class[] {});
            hasRequest = true;
        } catch (Exception e) {
            hasRequest = false;
        }
    }

    private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0);
    //id?
    private static final String SUFFIX_PAGE = "_PageHelper";
    //countid?
    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 String ORIGINAL_PARAMETER_OBJECT = "_ORIGINAL_PARAMETER_OBJECT";

    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

    private static final ReflectorFactory DEFAULT_REFLECTION_FACTORY = new DefaultReflectorFactory();

    /**
     * ??Mybatis?
     *
     * @param object ??
     * @return
     */
    private static MetaObject forObject(Object object) {
        return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,
                DEFAULT_REFLECTION_FACTORY);
    }

    //?SQL
    private static final SqlParser sqlParser = new SqlParser();
    //?parser
    private Parser parser;
    //?
    private Dialect dialect;

    //? - ??
    public enum Dialect {
        mysql, mariadb, sqlite, oracle, hsqldb, postgresql, sqlserver, db2
    }

    public static void setLocalPage(PageHelper page) {
        LOCAL_PAGE.set(page);
    }

    /**
     * ?Page?
     *
     * @return
     */
    private static PageHelper getLocalPage() {
        return LOCAL_PAGE.get();
    }

    /**
     * ??
     */
    private static void clearLocalPage() {
        LOCAL_PAGE.remove();
    }

    /**
     * ??
     *
     * @param params RowBounds?
     * @return Page
     */
    public PageHelper getPage(Object params) {
        PageHelper page = getLocalPage();
        if (page == null) {
            if (params instanceof RowBounds) {
                RowBounds rowBounds = (RowBounds) params;
                if (offsetAsPageNum) {
                    page = new PageHelper(rowBounds.getOffset(), rowBounds.getLimit(), rowBoundsWithCount);
                } else {
                    page = new PageHelper(rowBounds, rowBoundsWithCount);
                }
            } else {
                page = getPageFromObject(params);
            }
            setLocalPage(page);
        }
        //??
        if (page.getReasonable() == null) {
            page.setReasonable(reasonable);
        }
        //truepagesize0RowBoundslimit=0?
        if (page.getPageSizeZero() == null) {
            page.setPageSizeZero(pageSizeZero);
        }
        return page;
    }

    /**
     * ??
     *
     * @param params
     * @return
     */
    public static PageHelper getPageFromObject(Object params) {
        int pageNum;
        int pageSize;
        MetaObject paramsObject = null;
        if (params == null) {
            throw new NullPointerException("?params?!");
        }
        if (hasRequest && requestClass.isAssignableFrom(params.getClass())) {
            try {
                paramsObject = forObject(getParameterMap.invoke(params, new Object[] {}));
            } catch (Exception e) {
                //
            }
        } else {
            paramsObject = forObject(params);
        }
        if (paramsObject == null) {
            throw new NullPointerException("?params?!");
        }
        try {
            pageNum = Integer.parseInt(String.valueOf(getParamValue(paramsObject, "pageNum", true)));
            pageSize = Integer.parseInt(String.valueOf(getParamValue(paramsObject, "pageSize", true)));
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("???!");
        }
        Object _count = getParamValue(paramsObject, "count", false);
        boolean count = true;
        if (_count != null) {
            count = Boolean.valueOf(String.valueOf(_count));
        }
        PageHelper page = new PageHelper(pageNum, pageSize, count);
        Object reasonable = getParamValue(paramsObject, "reasonable", false);
        if (reasonable != null) {
            page.setReasonable(Boolean.valueOf(String.valueOf(reasonable)));
        }
        Object pageSizeZero = getParamValue(paramsObject, "pageSizeZero", false);
        if (pageSizeZero != null) {
            page.setPageSizeZero(Boolean.valueOf(String.valueOf(pageSizeZero)));
        }
        return page;
    }

    /**
     * ??
     *
     * @param paramsObject
     * @param paramName
     * @param required
     * @return
     */
    public static Object getParamValue(MetaObject paramsObject, String paramName, boolean required) {
        Object value = null;
        if (paramsObject.hasGetter(PARAMS.get(paramName))) {
            value = paramsObject.getValue(PARAMS.get(paramName));
        }
        if (required && value == null) {
            throw new RuntimeException("??:" + PARAMS.get(paramName));
        }
        return value;
    }

    /**
     * Mybatis
     *
     * @param invocation ?
     * @return 
     * @throws Throwable 
     */
    public Object processPage(Invocation invocation) throws Throwable {
        try {
            Object result = _processPage(invocation);
            clearLocalPage();
            //?spring
            if (result instanceof PageHelper) {
                //               MybatisPage page = (MybatisPage) result ;
                //               Page resultPage = new PageImpl(page , new PageRequest(page.getPageNum(), page.getPageSize()), page.getTotal()) ;
                //               return resultPage ;
                return new MybatisPageImpl((PageHelper) result);
            }
            return result;
        } finally {
            clearLocalPage();
        }
    }

    /**
     * Mybatis
     *
     * @param invocation ?
     * @return 
     * @throws Throwable 
     */
    private Object _processPage(Invocation invocation) throws Throwable {
        final Object[] args = invocation.getArgs();
        RowBounds rowBounds = (RowBounds) args[2];
        if (SqlUtil.getLocalPage() == null && rowBounds == RowBounds.DEFAULT) {
            return invocation.proceed();
        } else {
            //?ms
            MappedStatement ms = (MappedStatement) args[0];
            //RowBounds-?Mybatis
            args[2] = RowBounds.DEFAULT;
            //?
            PageHelper page = getPage(rowBounds);
            //pageSizeZero
            if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
                //?
                Object result = invocation.proceed();
                //?
                page.addAll((List) result);
                //
                page.setPageNum(1);
                //?pageSize=total
                page.setPageSize(page.size());
                //??total
                page.setTotal(page.size());
                //?Page - ???
                return page;
            }
            SqlSource sqlSource = ((MappedStatement) args[0]).getSqlSource();
            //?total??count
            if (page.isCount()) {
                //?MappedStatement?qs
                processCountMappedStatement(ms, sqlSource, args);
                //
                Object result = invocation.proceed();
                //
                page.setTotal((Integer) ((List) result).get(0));
                if (page.getTotal() == 0) {
                    return page;
                }
            }
            //pageSize>0pageSize<=0???count
            if (page.getPageSize() > 0 && ((rowBounds == RowBounds.DEFAULT && page.getPageNum() > 0)
                    || rowBounds != RowBounds.DEFAULT)) {
                //?MappedStatement?qs
                processPageMappedStatement(ms, sqlSource, page, args);
                //
                Object result = invocation.proceed();
                //?
                page.addAll((List) result);
            }
            //
            return page;
        }
    }

    public void setProperties(Properties p) {
        //offsetPageNum
        String offsetAsPageNum = p.getProperty("offsetAsPageNum");
        this.offsetAsPageNum = Boolean.parseBoolean(offsetAsPageNum);
        //RowBounds???count
        String rowBoundsWithCount = p.getProperty("rowBoundsWithCount");
        this.rowBoundsWithCount = Boolean.parseBoolean(rowBoundsWithCount);
        //truepagesize0RowBoundslimit=0?
        String pageSizeZero = p.getProperty("pageSizeZero");
        this.pageSizeZero = Boolean.parseBoolean(pageSizeZero);
        //??true?????false??
        String reasonable = p.getProperty("reasonable");
        this.reasonable = Boolean.parseBoolean(reasonable);
        //?
        PARAMS.put("pageNum", "pageNum");
        PARAMS.put("pageSize", "pageSize");
        PARAMS.put("count", "countSql");
        PARAMS.put("reasonable", "reasonable");
        PARAMS.put("pageSizeZero", "pageSizeZero");
        String params = p.getProperty("params");
        if (params != null && params.length() > 0) {
            String[] ps = params.split("[;|,|&]");
            for (String s : ps) {
                String[] ss = s.split("[=|:]");
                if (ss.length == 2) {
                    PARAMS.put(ss[0], ss[1]);
                }
            }
        }
    }

    /**
     * 
     *
     * @param strDialect
     */
    public SqlUtil(String strDialect) {
        if (strDialect == null || "".equals(strDialect)) {
            throw new IllegalArgumentException("Mybatis??dialect?!");
        }
        try {
            dialect = Dialect.valueOf(strDialect);
            parser = 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, PageHelper page) {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        return parser.setPageParameter(ms, parameterObject, boundSql, page);
    }

    /**
     * ?countMappedStatement
     *
     * @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, PageHelper 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 {

        /**
         * ??MappedStatement
         *
         * @return
         */
        boolean isSupportedMappedStatementCache();

        /**
         * ?sql - ????
         *
         * @param sql sql
         * @return countsql
         */
        String getCountSql(String sql);

        /**
         * ?sql - ????
         *
         * @param sql sql
         * @return sql
         */
        String getPageSql(String sql);

        /**
         * ??
         *
         * @param configuration
         * @param boundSql
         * @return
         */
        List<ParameterMapping> getPageParameterMapping(Configuration configuration, BoundSql boundSql);

        /**
         * ?
         *
         * @param ms
         * @param parameterObject
         * @param boundSql
         * @param page
         * @return
         */
        Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql, PageHelper page);
    }

    /**
     * Parser
     */
    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 sqlserver:
                parser = new SqlServerParser();
                break;
            case db2:
                parser = new Db2Parser();
                break;
            case postgresql:
            default:
                parser = new PostgreSQLParser();
            }
            return parser;
        }

        @Override
        public boolean isSupportedMappedStatementCache() {
            return true;
        }

        public String getCountSql(final String sql) {
            return sqlParser.getSmartCountSql(sql);
        }

        public abstract String getPageSql(String sql);

        public List<ParameterMapping> getPageParameterMapping(Configuration configuration, BoundSql boundSql) {
            List<ParameterMapping> newParameterMappings = new ArrayList<ParameterMapping>();
            if (boundSql.getParameterMappings() != null) {
                newParameterMappings.addAll(boundSql.getParameterMappings());
            }
            newParameterMappings
                    .add(new ParameterMapping.Builder(configuration, PAGEPARAMETER_FIRST, Integer.class).build());
            newParameterMappings
                    .add(new ParameterMapping.Builder(configuration, PAGEPARAMETER_SECOND, Integer.class).build());
            return newParameterMappings;
        }

        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = null;
            if (parameterObject == null) {
                paramMap = new HashMap();
            } else if (parameterObject instanceof Map) {
                paramMap = (Map) parameterObject;
            } else {
                paramMap = new HashMap();
                //?sql??ParameterMapping?getter
                //TypeHandlerRegistry???
                boolean hasTypeHandler = ms.getConfiguration().getTypeHandlerRegistry()
                        .hasTypeHandler(parameterObject.getClass());
                MetaObject metaObject = forObject(parameterObject);
                //??MyProviderSqlSource?
                if (ms.getSqlSource() instanceof MyProviderSqlSource) {
                    paramMap.put(PROVIDER_OBJECT, parameterObject);
                }
                if (!hasTypeHandler) {
                    for (String name : metaObject.getGetterNames()) {
                        paramMap.put(name, metaObject.getValue(name));
                    }
                }
                //????
                if (boundSql.getParameterMappings() != null && boundSql.getParameterMappings().size() > 0) {
                    for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
                        String name = parameterMapping.getProperty();
                        if (!name.equals(PAGEPARAMETER_FIRST) && !name.equals(PAGEPARAMETER_SECOND)
                                && paramMap.get(name) == null) {
                            if (hasTypeHandler
                                    || parameterMapping.getJavaType().equals(parameterObject.getClass())) {
                                paramMap.put(name, parameterObject);
                                break;
                            }
                        }
                    }
                }
            }
            //?
            paramMap.put(ORIGINAL_PARAMETER_OBJECT, parameterObject);
            return paramMap;
        }
    }

    //Mysql
    private static class MysqlParser extends SimpleParser {
        @Override
        public String getPageSql(String sql) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
            sqlBuilder.append(sql);
            sqlBuilder.append(" limit ?,?");
            return sqlBuilder.toString();
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = super.setPageParameter(ms, parameterObject, boundSql, page);
            paramMap.put(PAGEPARAMETER_FIRST, page.getStartRow());
            paramMap.put(PAGEPARAMETER_SECOND, page.getPageSize());
            return paramMap;
        }
    }

    //Oracle
    private static class OracleParser extends SimpleParser {
        @Override
        public String getPageSql(String sql) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);
            sqlBuilder.append("select * from ( select tmp_page.*, rownum row_id from ( ");
            sqlBuilder.append(sql);
            sqlBuilder.append(" ) tmp_page where rownum <= ? ) where row_id > ?");
            return sqlBuilder.toString();
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = super.setPageParameter(ms, parameterObject, boundSql, page);
            paramMap.put(PAGEPARAMETER_FIRST, page.getEndRow());
            paramMap.put(PAGEPARAMETER_SECOND, page.getStartRow());
            return paramMap;
        }
    }

    //Hsqldb
    private static class HsqldbParser extends SimpleParser {
        @Override
        public String getPageSql(String sql) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 20);
            sqlBuilder.append(sql);
            sqlBuilder.append(" limit ? offset ?");
            return sqlBuilder.toString();
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = super.setPageParameter(ms, parameterObject, boundSql, page);
            paramMap.put(PAGEPARAMETER_FIRST, page.getPageSize());
            paramMap.put(PAGEPARAMETER_SECOND, page.getStartRow());
            return paramMap;
        }
    }

    //PostgreSQL
    private static class PostgreSQLParser extends SimpleParser {
        @Override
        public String getPageSql(String sql) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
            sqlBuilder.append(sql);
            sqlBuilder.append(" limit ? offset ?");
            return sqlBuilder.toString();
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = super.setPageParameter(ms, parameterObject, boundSql, page);
            paramMap.put(PAGEPARAMETER_FIRST, page.getPageSize());
            paramMap.put(PAGEPARAMETER_SECOND, page.getStartRow());
            return paramMap;
        }
    }

    //SqlServer
    private static class SqlServerParser extends SimpleParser {
        private static final SqlServer pageSql = new SqlServer();

        @Override
        public boolean isSupportedMappedStatementCache() {
            //sqlserver??sql??MS
            return false;
        }

        @Override
        public List<ParameterMapping> getPageParameterMapping(Configuration configuration, BoundSql boundSql) {
            return boundSql.getParameterMappings();
        }

        @Override
        public String getPageSql(String sql) {
            PageHelper page = getLocalPage();
            return pageSql.convertToPageSql(sql, page.getStartRow(), page.getPageSize(), page.getOrderBy());
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            return super.setPageParameter(ms, parameterObject, boundSql, page);
        }
    }

    //Db2Parser
    private static class Db2Parser extends SimpleParser {
        @Override
        public String getPageSql(String sql) {
            StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120);
            sqlBuilder.append("select * from (select tmp_page.*,rownumber() over() as row_id from ( ");
            sqlBuilder.append(sql);
            sqlBuilder.append(" ) as tmp_page) where row_id between  ? and ?");
            return sqlBuilder.toString();
        }

        @Override
        public Map setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql,
                PageHelper page) {
            Map paramMap = super.setPageParameter(ms, parameterObject, boundSql, page);
            paramMap.put(PAGEPARAMETER_FIRST, page.getStartRow() + 1);
            paramMap.put(PAGEPARAMETER_SECOND, page.getEndRow());
            return paramMap;
        }
    }

    /**
     * ?SqlSource
     */
    private class MyDynamicSqlSource implements SqlSource {
        private Configuration configuration;
        private SqlNode rootSqlNode;
        /**
         * ?count
         */
        private Boolean count;

        public MyDynamicSqlSource(Configuration configuration, SqlNode rootSqlNode, Boolean count) {
            this.configuration = configuration;
            this.rootSqlNode = rootSqlNode;
            this.count = count;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            DynamicContext context;
            //??parameterObject???
            //??Map?KEY
            //bug#25:http://git.oschina.net/free/Mybatis_PageHelper/issues/25
            if (parameterObject != null && parameterObject instanceof Map
                    && ((Map) parameterObject).containsKey(ORIGINAL_PARAMETER_OBJECT)) {
                context = new DynamicContext(configuration, ((Map) parameterObject).get(ORIGINAL_PARAMETER_OBJECT));
            } else {
                context = new DynamicContext(configuration, parameterObject);
            }
            rootSqlNode.apply(context);
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
            SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
            if (count) {
                sqlSource = getCountSqlSource(configuration, sqlSource, parameterObject);
            } else {
                sqlSource = getPageSqlSource(configuration, sqlSource, parameterObject);
            }
            BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
            //??
            for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
                boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
            }
            return boundSql;
        }
    }

    /**
     * ProviderSqlSource?
     */
    private class MyProviderSqlSource implements SqlSource {
        private Configuration configuration;
        private ProviderSqlSource providerSqlSource;

        /**
         * ?count
         */
        private Boolean count;

        private MyProviderSqlSource(Configuration configuration, ProviderSqlSource providerSqlSource,
                Boolean count) {
            this.configuration = configuration;
            this.providerSqlSource = providerSqlSource;
            this.count = count;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            BoundSql boundSql = null;
            if (parameterObject instanceof Map && ((Map) parameterObject).containsKey(PROVIDER_OBJECT)) {
                boundSql = providerSqlSource.getBoundSql(((Map) parameterObject).get(PROVIDER_OBJECT));
            } else {
                boundSql = providerSqlSource.getBoundSql(parameterObject);
            }
            if (count) {
                return new BoundSql(configuration, parser.getCountSql(boundSql.getSql()),
                        boundSql.getParameterMappings(), parameterObject);
            } else {
                return new BoundSql(configuration, parser.getPageSql(boundSql.getSql()),
                        parser.getPageParameterMapping(configuration, boundSql), parameterObject);
            }
        }
    }

    /**
     * ?ms - ms???
     *
     * @param ms
     * @param sqlSource
     * @param suffix
     * @return
     */
    private MappedStatement getMappedStatement(MappedStatement ms, SqlSource sqlSource, Object parameterObject,
            String suffix) {
        MappedStatement qs = null;
        if (ms.getId().endsWith(SUFFIX_PAGE) || ms.getId().endsWith(SUFFIX_COUNT)) {
            throw new RuntimeException(
                    "??:????(Spring,mybatis-config.xmlSpring<bean>????????)?");
        }
        if (parser.isSupportedMappedStatementCache()) {
            try {
                qs = ms.getConfiguration().getMappedStatement(ms.getId() + suffix);
            } catch (Exception e) {
                //ignore
            }
        }
        if (qs == null) {
            //MappedStatement
            qs = newMappedStatement(ms, getsqlSource(ms, sqlSource, parameterObject, suffix), suffix);
            if (parser.isSupportedMappedStatementCache()) {
                try {
                    ms.getConfiguration().addMappedStatement(qs);
                } catch (Exception e) {
                    //ignore
                }
            }
        }
        return qs;
    }

    /**
     * countMappedStatement
     *
     * @param ms
     * @param sqlSource
     * @param suffix
     * @return
     */
    private MappedStatement newMappedStatement(MappedStatement ms, SqlSource sqlSource, String suffix) {
        String id = ms.getId() + suffix;
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), id, sqlSource,
                ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        if (suffix == SUFFIX_PAGE) {
            builder.resultMaps(ms.getResultMaps());
        } else {
            //countint
            List<ResultMap> resultMaps = new ArrayList<ResultMap>();
            ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), id, int.class, EMPTY_RESULTMAPPING)
                    .build();
            resultMaps.add(resultMap);
            builder.resultMaps(resultMaps);
        }
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

    /**
     * ???sql
     *
     * @param ms
     * @return
     */
    public boolean isDynamic(MappedStatement ms) {
        return ms.getSqlSource() instanceof DynamicSqlSource;
    }

    /**
     * ?sqlSource
     *
     * @param ms
     * @param sqlSource
     * @param parameterObject
     * @param suffix
     * @return
     */
    private SqlSource getsqlSource(MappedStatement ms, SqlSource sqlSource, Object parameterObject, String suffix) {
        //1. XMLLanguageDriver.javaXMLScriptBuilder.java???SqlSource
        //2. ProviderSqlSource
        //3. RawSqlSource?
        //?sql
        if (isDynamic(ms)) {
            MetaObject msObject = forObject(ms);
            SqlNode sqlNode = (SqlNode) msObject.getValue("sqlSource.rootSqlNode");
            MixedSqlNode mixedSqlNode = null;
            if (sqlNode instanceof MixedSqlNode) {
                mixedSqlNode = (MixedSqlNode) sqlNode;
            } else {
                List<SqlNode> contents = new ArrayList<SqlNode>(1);
                contents.add(sqlNode);
                mixedSqlNode = new MixedSqlNode(contents);
            }
            return new MyDynamicSqlSource(ms.getConfiguration(), mixedSqlNode, suffix == SUFFIX_COUNT);
        } else if (sqlSource instanceof ProviderSqlSource) {
            return new MyProviderSqlSource(ms.getConfiguration(), (ProviderSqlSource) sqlSource,
                    suffix == SUFFIX_COUNT);
        }
        //??sql
        else if (suffix == SUFFIX_PAGE) {
            //sql
            return getPageSqlSource(ms.getConfiguration(), sqlSource, parameterObject);
        }
        //??count-sql
        else {
            return getCountSqlSource(ms.getConfiguration(), sqlSource, parameterObject);
        }
    }

    /**
     * ?sqlSource
     *
     * @param configuration
     * @param sqlSource
     * @return
     */
    private SqlSource getPageSqlSource(Configuration configuration, SqlSource sqlSource, Object parameterObject) {
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        return new StaticSqlSource(configuration, parser.getPageSql(boundSql.getSql()),
                parser.getPageParameterMapping(configuration, boundSql));
    }

    /**
     * ?countsqlSource
     *
     * @param sqlSource
     * @return
     */
    private SqlSource getCountSqlSource(Configuration configuration, SqlSource sqlSource, Object parameterObject) {
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        return new StaticSqlSource(configuration, parser.getCountSql(boundSql.getSql()),
                boundSql.getParameterMappings());
    }

    /**
     * [?]countsql
     *
     * @param dialet      ?
     * @param originalSql sql
     */
    public static void testSql(String dialet, String originalSql) {
        SqlUtil sqlUtil = new SqlUtil(dialet);
        if (sqlUtil.dialect == Dialect.sqlserver) {
            setLocalPage(new PageHelper(1, 10));
        }
        String countSql = sqlUtil.parser.getCountSql(originalSql);
        System.out.println(countSql);
        String pageSql = sqlUtil.parser.getPageSql(originalSql);
        System.out.println(pageSql);
    }
}