1. 程式人生 > >MyBatis 物理分頁foreach 參數失效

MyBatis 物理分頁foreach 參數失效

list min div title count map() 數據 private lec

MyBatis-3.4.4.jar使用分頁插件時並且查詢條件包含foreach時,分頁插件在執行count語句時會拋出異常,報參數為空異常。分頁插件會新增一個COUNT的SQL,並復制原BoundSql對象,然後使用DefaultParameterHandler.setParameters給COUNT語句設值。foreach過程中產生的參數變量是放在AdditionalParameter中,但復制BoundSql時並沒有復制其中的additionalParameters字段,而由foreach產生的參數是存放在additionalParameters中,所以導致參數空異常。解決方案就是反射獲取到additionalParameters字段的值並設置到新產生的BoundSql中去。

// 只攔截select部分
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class PaginationInterceptor implements Interceptor {

    private Logger logger = LoggerFactory.getLogger(PaginationInterceptor.class
); Dialect dialect = new MySql5Dialect(); public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = invocation.getArgs()[1]; BoundSql boundSql = mappedStatement.getBoundSql(parameter); String originalSql
= boundSql.getSql().trim(); RowBounds rowBounds = (RowBounds) invocation.getArgs()[2]; Object parameterObject = boundSql.getParameterObject(); if (boundSql == null || boundSql.getSql() == null || "".equals(boundSql.getSql())) return null; // 分頁參數--上下文傳參 PageInfo page = null; // map傳參每次都將currentPage重置,先判讀map再判斷context if (parameterObject instanceof PageInfo) { page = (PageInfo) parameterObject; } else if (parameterObject instanceof Map) { Map<String, Object> map = (Map<String, Object>) parameterObject; if (map.containsKey("page")) { page = (PageInfo) map.get("page"); } } else if (null != parameterObject) { Field pageField = ReflectionUtil.getFieldByFieldName(parameterObject, "page"); if (pageField != null) { page = (PageInfo) ReflectionUtil.getValueByFieldName(parameterObject, "page"); } } // 後面用到了context的東東 if (page != null && page.isPagination() == true) { if (page.getPageSize() > 10000) { // throw new Exception("pageSize不能大於10000"); logger.warn("pageSize建議不要大於10000,當前sqlId:{},page:{}", mappedStatement.getId(), JSON.toJSONString(page)); } int totalRows = page.getTotalRows(); // 得到總記錄數 if (totalRows == 0 && page.isNeedCount()) { StringBuffer countSql = new StringBuffer(); countSql.append(MySql5PageHepler.getCountString(originalSql)); Connection connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection(); PreparedStatement countStmt = connection.prepareStatement(countSql.toString()); BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql.toString(), boundSql.getParameterMappings(), parameterObject); Field metaParamsField = ReflectionUtil.getFieldByFieldName(boundSql, "metaParameters"); if (metaParamsField != null) { MetaObject mo = (MetaObject) ReflectionUtil.getValueByFieldName(boundSql, "metaParameters"); ReflectionUtil.setValueByFieldName(countBS, "metaParameters", mo); } Field additionalField = ReflectionUtil.getFieldByFieldName(boundSql, "additionalParameters"); if (additionalField != null) { Map<String, Object> map = (Map<String, Object>) ReflectionUtil.getValueByFieldName(boundSql, "additionalParameters"); ReflectionUtil.setValueByFieldName(countBS, "additionalParameters", map); } setParameters(countStmt, mappedStatement, countBS, parameterObject); ResultSet rs = countStmt.executeQuery(); if (rs.next()) { totalRows = rs.getInt(1); } rs.close(); countStmt.close(); connection.close(); } // 分頁計算 page.init(totalRows, page.getPageSize(), page.getCurrentPage()); if (rowBounds == null || rowBounds == RowBounds.DEFAULT) { rowBounds = new RowBounds(page.getPageSize() * (page.getCurrentPage() - 1), page.getPageSize()); } // 分頁查詢 本地化對象 修改數據庫註意修改實現 String pagesql = dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()); invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT); BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), pagesql, boundSql.getParameterMappings(), boundSql.getParameterObject()); Field metaParamsField = ReflectionUtil.getFieldByFieldName(boundSql, "metaParameters"); if (metaParamsField != null) { MetaObject mo = (MetaObject) ReflectionUtil.getValueByFieldName(boundSql, "metaParameters"); ReflectionUtil.setValueByFieldName(newBoundSql, "metaParameters", mo); } Field additionalField = ReflectionUtil.getFieldByFieldName(boundSql, "additionalParameters"); if (additionalField != null) { Map<String, Object> map = (Map<String, Object>) ReflectionUtil.getValueByFieldName(boundSql, "additionalParameters"); ReflectionUtil.setValueByFieldName(newBoundSql, "additionalParameters", map); } MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql)); invocation.getArgs()[0] = newMs; } return invocation.proceed(); } public static class BoundSqlSqlSource implements SqlSource { BoundSql boundSql; public BoundSqlSqlSource(BoundSql boundSql) { this.boundSql = boundSql; } public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } public Object plugin(Object arg0) { return Plugin.wrap(arg0, this); } public void setProperties(Properties arg0) { } /** * 對SQL參數(?)設值,參考org.apache.ibatis.executor.parameter.DefaultParameterHandler * * @param ps * @param mappedStatement * @param boundSql * @param parameterObject * @throws SQLException */ private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { Configuration configuration = mappedStatement.getConfiguration(); TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject); for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); PropertyTokenizer prop = new PropertyTokenizer(propertyName); if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) { value = boundSql.getAdditionalParameter(prop.getName()); if (value != null) { value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length())); } } else { value = metaObject == null ? null : metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); if (typeHandler == null) { throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId()); } typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType()); } } } } private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) { Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); builder.keyProperty(buildKeyProperty(ms.getKeyProperties())); builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.useCache(ms.isUseCache()); builder.cache(ms.getCache()); MappedStatement newMs = builder.build(); return newMs; } private static String buildKeyProperty(String[] props) { if (null != props && props.length > 0) { StringBuffer sb = new StringBuffer(); for (String p : props) { sb.append(p).append(","); } return sb.substring(0, sb.length() - 1); } return null; } }

下面是:ReflectUtil

public class ReflectionUtil
{
    private static final Log logger = LogFactory.getLog(ReflectionUtil.class);

    public static void setFieldValue(Object object, String fieldName, Object value)
    {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);
        try
        {
            field.set(object, value);
        }
        catch (IllegalAccessException e)
        {
        }
    }

    public static Object getFieldValue(Object object, String fieldName)
    {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);

        Object result = null;
        try
        {
            result = field.get(object);
        }
        catch (IllegalAccessException e)
        {
        }

        return result;
    }

    public static Object invokeMethod(Object object, String methodName, Class<?>[] parameterTypes, Object[] parameters)
            throws InvocationTargetException
    {
        Method method = getDeclaredMethod(object, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
        }
        method.setAccessible(true);
        try
        {
            return method.invoke(object, parameters);
        }
        catch (IllegalAccessException e)
        {
        }

        return null;
    }

    protected static Field getDeclaredField(Object object, String fieldName)
    {
        for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
        {
            try
            {
                return superClass.getDeclaredField(fieldName);
            }
            catch (NoSuchFieldException e)
            {
            }
        }
        return null;
    }

    protected static void makeAccessible(Field field)
    {
        if ((!Modifier.isPublic(field.getModifiers())) || (!Modifier.isPublic(field.getDeclaringClass().getModifiers())))
        {
            field.setAccessible(true);
        }
    }

    protected static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes)
    {
        for (Class superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
        {
            try
            {
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            }
            catch (NoSuchMethodException e)
            {
            }
        }

        return null;
    }

    public static <T> Class<T> getSuperClassGenricType(Class clazz)
    {
        return getSuperClassGenricType(clazz, 0);
    }

    public static Class getSuperClassGenricType(Class clazz, int index)
    {
        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType))
        {
            logger.warn(clazz.getSimpleName() + "‘s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType)genType).getActualTypeArguments();

        if ((index >= params.length) || (index < 0))
        {
            logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "‘s Parameterized Type: " + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class))
        {
            logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }

        return (Class)params[index];
    }

    public static IllegalArgumentException convertToUncheckedException(Exception e)
    {
        if (((e instanceof IllegalAccessException)) || ((e instanceof IllegalArgumentException)) || ((e instanceof NoSuchMethodException))) {
            return new IllegalArgumentException("Refelction Exception.", e);
        }
        return new IllegalArgumentException(e);
    }

    public static Field getFieldByFieldName(Object obj, String fieldName)
    {
        for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
        {
            try
            {
                return superClass.getDeclaredField(fieldName);
            }
            catch (NoSuchFieldException e)
            {
            }
        }
        return null;
    }

    public static Object getValueByFieldName(Object obj, String fieldName)
            throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
    {
        Field field = getFieldByFieldName(obj, fieldName);
        Object value = null;
        if (field != null)
        {
            if (field.isAccessible())
            {
                value = field.get(obj);
            }
            else
            {
                field.setAccessible(true);
                value = field.get(obj);
                field.setAccessible(false);
            }
        }
        return value;
    }

    public static void setValueByFieldName(Object obj, String fieldName, Object value)
            throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
    {
        Field field = obj.getClass().getDeclaredField(fieldName);
        if (field.isAccessible())
        {
            field.set(obj, value);
        }
        else
        {
            field.setAccessible(true);
            field.set(obj, value);
            field.setAccessible(false);
        }
    }
}

參考:

http://blog.csdn.net/synsdeng/article/details/78561139

http://blog.csdn.net/flamingsky007/article/details/7195399

http://fred-han.iteye.com/blog/1771395

可以在github上看,作者對這個問題的回復

https://github.com/mybatis/mybatis-3/issues/1074

MyBatis 物理分頁foreach 參數失效