MyBatis 物理分页foreach 参数失效(list值传不进<foreach>标签为null)

本文介绍了解决MyBatis分页插件在处理包含foreach查询时出现的参数丢失问题的方法。通过自定义拦截器,实现了对分页查询中additionalParameters的支持,确保了查询的正确性。

前些日子遇到一个mybatis中进行分页查询的问题,进行分页查询时查询条件传入了一个列表,在mybatis Mapper.xml中表现为foreach处理,查询记录总数时list可以传值进去,但是查询具体某个分页数据时list中的参数死活传不进去。直到采用下面文章中提到的处理才解决。

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);
                }
                
                //-------------------------------------------------------//
                //大部分网上的资料缺少对additionalParameters处理,这部分很关键//
                //-------------------------------------------------------//
                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);
            }

                //-------------------------------------------------------//
                //大部分网上的资料缺少对additionalParameters处理,这部分很关键//
                //-------------------------------------------------------//

            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.youkuaiyun.com/synsdeng/article/details/78561139

http://blog.youkuaiyun.com/flamingsky007/article/details/7195399

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

 

可以在github上看,作者对这个问题的回复

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

 

转自:https://www.cnblogs.com/chn58/p/8125705.html

 

foreach 内嵌入 <trim prefix="(" suffix=")" suffixOverrides=","> <if test="searchValue != null">searchvalue,</if> <if test="createBy != null">createby,</if> <if test="createTime != null">createtime,</if> <if test="updateBy != null">updateby,</if> <if test="updateTime != null">updatetime,</if> <if test="remark != null">remark,</if> <if test="params != null">params,</if> <if test="id != null">id,</if> <if test="serialId != null and serialId != ''">serialid,</if> <if test="purchaserOrganizationId != null and purchaserOrganizationId != ''">purchaserorganizationid,</if> <if test="organizationId != null and organizationId != ''">organizationid,</if> <if test="vehicleId != null and vehicleId != ''">vehicleid,</if> <if test="purchaserOrganizationCode != null">purchaserorganizationcode,</if> <if test="erpPlantCd != null">erpplantcd,</if> <if test="materialCode != null">materialcode,</if> <if test="vehicleSeries != null">vehicleseries,</if> <if test="batchNo != null and batchno != ''">batchno,</if> <if test="arrivalYymm != null and arrivalYymm != ''">arrivalyymm,</if> <if test="quantity != null and quantity != ''">quantity,</if> <if test="contractNo != null and contractNo != ''">contractno,</if> <if test="lotNo != null and lotNo != ''">lotno,</if> <if test="itemId != null and itemId != ''">itemid,</if> <if test="itemCode != null">itemcode,</if> <if test="partDiv != null">partdiv,</if> <if test="kdType != null">kdtype,</if> <if test="pushFlag != null">pushflag,</if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="searchValue != null">#{searchValue},</if> <if test="createBy != null">#{createBy},</if> <if test="createTime != null">#{createTime},</if> <if test="updateBy != null">#{updateBy},</if> <if test="updateTime != null">#{updateTime},</if> <if test="remark != null">#{remark},</if> <if test="params != null">#{params},</if> <if test="id != null">#{id},</if> <if test="serialId != null and serialId != ''">#{serialId},</if> <if test="purchaserOrganizationId != null and purchaserOrganizationId != ''">#{purchaserOrganizationId},</if> <if test="organizationId != null and organizationId != ''">#{organizationId},</if> <if test="vehicleId != null and vehicleId != ''">#{vehicleId},</if> <if test="purchaserOrganizationCode != null">#{purchaserOrganizationCode},</if> <if test="erpPlantCd != null">#{erpPlantCd},</if> <if test="materialCode != null">#{materialCode},</if> <if test="vehicleSeries != null">#{vehicleSeries},</if> <if test="batchNo != null and batchNo != ''">#{batchNo},</if> <if test="arrivalYymm != null and arrivalYymm != ''">#{arrivalYymm},</if> <if test="quantity != null and quantity != ''">#{quantity},</if> <if test="contractNo != null and contractNo != ''">#{contractNo},</if> <if test="lotNo != null and lotNo != ''">#{lotNo},</if> <if test="itemId != null and itemId != ''">#{itemId},</if> <if test="itemCode != null">#{itemCode},</if> <if test="partDiv != null">#{partDiv},</if> <if test="kdType != null">#{kdType},</if> <if test="pushFlag != null">#{pushFlag},</if> </trim>
最新发布
11-15
UPDATE SIEBEL.CX_ORDER_ITEM <set> <trim prefix="POLICY_BASE_RULE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.policyBaseRule != null and item.policyBaseRule !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.policyBaseRule} </if> </foreach> ELSE POLICY_BASE_RULE </trim> <trim prefix="PACKAGE_NAME = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.packageName != null and item.packageName !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.packageName} </if> </foreach> ELSE PACKAGE_NAME </trim> <trim prefix="PACKAGE_PRICE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.packagePrice != null and item.packagePrice !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.packagePrice} </if> </foreach> ELSE PACKAGE_PRICE </trim> <trim prefix="ACCOUNTING_PRICE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.accountingPrice != null and item.accountingPrice !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.accountingPrice} </if> </foreach> ELSE ACCOUNTING_PRICE </trim> <trim prefix="QUANTITY = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.quantity != null and item.quantity!=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.quantity} </if> </foreach> ELSE QUANTITY </trim> <trim prefix="CONTRACT_PRICE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.contractPrice != null and item.contractPrice !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.contractPrice} </if> </foreach> ELSE CONTRACT_PRICE </trim> <trim prefix="SETTLE_PRICE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.settlePrice != null and item.settlePrice !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.settlePrice} </if> </foreach> ELSE SETTLE_PRICE </trim> <trim prefix="PACKAGE_CODE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.packageCode != null and item.packageCode !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.packageCode} </if> </foreach> ELSE PACKAGE_CODE </trim> <trim prefix="SALES_PRICE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.salesPrice != null and item.salesPrice !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.salesPrice} </if> </foreach> ELSE SALES_PRICE </trim> <trim prefix="POLICY_TYPE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.policyType != null and item.policyType !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.policyType} </if> </foreach> ELSE POLICY_TYPE </trim> <trim prefix="CONTRACT_CODE = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.contractCode != null and item.contractCode !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.contractCode} </if> </foreach> ELSE CONTRACT_CODE </trim> <trim prefix="LOGISTICS_STATUS = CASE" suffix="END,"> <foreach collection="list" item="item"> <if test="item.logisticsStatus != null and item.logisticsStatus !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.logisticsStatus} </if> </foreach> ELSE LOGISTICS_STATUS </trim> <trim prefix="SETTLE_STATUS = CASE" suffix="END"> <foreach collection="list" item="item"> <if test="item.settleStatus != null and item.settleStatus !=''"> WHEN ROW_ID = #{item.rowId} THEN #{item.settleStatus} </if> </foreach> ELSE SETTLE_STATUS </trim> </set> WHERE ROW_ID IN <foreach collection="list" item="item" open="(" separator="," close=")"> #{item.rowId} </foreach> </update> 这样有啥问题 对应ORACLE
07-17
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值