All Downloads are FREE. Search and download functionalities are using the official Maven repository.

norm.support.mybatis.page.PagePlugin Maven / Gradle / Ivy

There is a newer version: 2.4
Show newest version
package norm.support.mybatis.page;
import norm.page.Filter;
import norm.page.Page;
import norm.page.PageSql;
import norm.page.impl.*;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.*;

/**
 * MyBatis的分页插件,放在这里给大家使用
 */
public class PagePlugin implements Interceptor{

    private static Map pageSqlMap = new HashMap();


    private String database;


    public static void registerPageSql(PageSql pageSql){
        if(pageSql == null){
            throw new IllegalArgumentException();
        }
        String db = pageSql.database();
        if(db == null){
            throw new PluginException("the database of pagesql is null!");
        }
        pageSqlMap.put(db.toLowerCase(),pageSql);
    }


    static {
        registerPageSql(new Db2Page());
        registerPageSql(new DerbyPage());
        registerPageSql(new H2Page());
        registerPageSql(new MySQLPage());
        registerPageSql(new OraclePage());
        registerPageSql(new PostgreSQLPage());
        registerPageSql(new SQLServerPage());
    }



    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Page page = PageHelper.getPage();
        if(page == null){
            return invocation.proceed();
        }
        if(invocation.getTarget() instanceof StatementHandler){
            StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
            // 可以分离出最原始的的目标类)
            while (metaStatementHandler.hasGetter("h")) {
                Object object = metaStatementHandler.getValue("h");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }
            // 分离最后一个代理对象的目标类
            while (metaStatementHandler.hasGetter("target")) {
                Object object = metaStatementHandler.getValue("target");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }
            MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            Filter filter = PageHelper.getFilter();

            BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
            String sql = boundSql.getSql();

            if(sql.toLowerCase().startsWith("select") && (filter == null || filter.accept((StatementHandler) metaStatementHandler.getValue("delegate")))){

                String pageSql = this.buildPageSql(page,sql);
                //重写分页sql
                metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);

                //计算page count
                this.evalPageCount(sql, invocation, mappedStatement, boundSql, page);
            }

            return invocation.proceed();
        }else if(invocation.getTarget() instanceof ResultSetHandler){
            Object result = invocation.proceed();
            if(page.isCollectResult()){
                page.setResult(result);
            }
            return result;
        }
        //如果抛出这个异常 你可以去买彩票了
        throw new IllegalStateException("invocation error!");
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
            Class type = target.getClass();
            Map, Set> signatureMap = getSignatureMap(type);
            Class[] interfaces = getAllInterfaces(type, signatureMap);
            if (interfaces.length > 0) {
                return Proxy.newProxyInstance(
                        type.getClassLoader(),
                        interfaces,
                        new Plugin(target, this, signatureMap));
            }
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
        String database = properties.getProperty("database");
        if(database == null || database.isEmpty()){
            throw new RuntimeException("null database property!");
        }
        this.database = database;
    }

    private String buildPageSql(Page page,String sql){
        PageSql pageSql = pageSqlMap.get(this.database.toLowerCase());
        if(pageSql == null){
            throw new PluginException("not support database :" + this.database);
        }
        return pageSql.buildSql(page,sql);
    }

    private void evalPageCount(String sql, Invocation invocation, MappedStatement mappedStatement,
                              BoundSql boundSql, Page page){
        if(page.isEvalCount()){
            String countSql = "select count(1) from ( " + sql + " )";
            Connection connection = (Connection) invocation.getArgs()[0];
            PreparedStatement ps = null;
            ResultSet rs = null;
            try{
                ps = connection.prepareStatement(countSql);
                BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
                        boundSql.getParameterMappings(), boundSql.getParameterObject());
                setParameters(ps, mappedStatement, countBS, boundSql.getParameterObject());
                rs = ps.executeQuery();
                int totalCount = 0;
                if (rs.next()) {
                    totalCount = rs.getInt(1);
                }
                page.setTotal(totalCount);
                int pageSize = page.getPageSize();
                page.setPageCount(totalCount / pageSize + ((totalCount % pageSize == 0) ? 0 : 1));
            }catch (SQLException e){
                throw new PersistenceException("eval page count error.sql:" + countSql,e);
            }finally {
                if(rs != null){
                    try {
                        rs.close();
                    } catch (SQLException e) {

                    }
                }
                if(ps != null){
                    try {
                        ps.close();
                    } catch (SQLException e) {

                    }
                }
            }

        }
    }

    private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
                               Object parameterObject) throws SQLException {
        ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler.setParameters(ps);
    }


    //这里跟mybatis原生的Plugin.wrap方法原理类似,不过这里是采取手动代理的方式,而不是采取注解
    //这样,可兼容mybatis所有版本,具体参考mybatis中Plugin类的源码
    private Map, Set> getSignatureMap(Class type){
        Map, Set> signatureMap = new HashMap, Set>();
        //处理StatementHandler prepare方法
        handleMethodSet(signatureMap,StatementHandler.class,"prepare");
        //处理ResultSetHandler handleResultSets方法
        handleMethodSet(signatureMap,ResultSetHandler.class,"handleResultSets");
        return signatureMap;
    }

    private static void handleMethodSet(Map, Set> signatureMap,Class type,String methodName){
        Method[] methods = type.getDeclaredMethods();
        Set set = new HashSet();
        for(Method method : methods){
            if(method.getName().equals(methodName)){
                set.add(method);
            }
        }
        if(set.isEmpty()){
            throw new PluginException("no such method on [" + type + "] name:" + methodName);
        }
        signatureMap.put(type,set);
    }


    private static Class[] getAllInterfaces(Class type, Map, Set> signatureMap) {
        Set> interfaces = new HashSet>();
        while (type != null) {
            for (Class c : type.getInterfaces()) {
                if (signatureMap.containsKey(c)) {
                    interfaces.add(c);
                }
            }
            type = type.getSuperclass();
        }
        return interfaces.toArray(new Class[interfaces.size()]);
    }


    private static class Plugin implements InvocationHandler {
        private final Object target;
        private final Interceptor interceptor;
        private final Map, Set> signatureMap;

        public Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) {
            this.target = target;
            this.interceptor = interceptor;
            this.signatureMap = signatureMap;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                Set methods = signatureMap.get(method.getDeclaringClass());
                if (methods != null && methods.contains(method)) {
                    return interceptor.intercept(new Invocation(target, method, args));
                }
                return method.invoke(target, args);
            } catch (Exception e) {
                throw ExceptionUtil.unwrapThrowable(e);
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy