mybatis3.2.8踩坑记录之.size()

2019-07-29  本文已影响0人  kevin0016

关于mybatis的xml标签使用问题和单元测试模拟高并发场景

标签使用问题

线上问题复现

Exception in thread "Thread-10" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'null!=list and list.size()>0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [3] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class com.google.common.collect.Lists$TransformingRandomAccessList with modifiers "public"]
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368)
    at com.sun.proxy.$Proxy26.selectList(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198)

查询对应的xml

<select id="findUserProductDelaysByOrderNoAndProductId"
        resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from pe_user_product_delay where is_deleted = 0
        <if test="null!=list and list.size()>0">
            and status in
            <foreach collection="list" index="index" item="status" open="("
                separator="," close=")">
                #{status,jdbcType=INTEGER}
            </foreach>
        </if>
        <if test="productId != null and productId >= 0">
            and product_id =#{productId,jdbcType=INTEGER}
        </if>
        <if test="orderNo != null and orderNo !=''">
            and order_no =#{orderNo,jdbcType=VARCHAR}
        </if>
        and type in (1,2,3)
    </select>

问题就出现在list.size()这上面

目前的mybatis版本为3.2.8,框架对于传入的集合的调用size方法这部分并发控制的并不好,导致并发量稍微一高就会导致抛出异常,这里有一个关于这个问题的说明

https://zhuanlan.zhihu.com/p/30085658

此处建议:

不要在mybatis的xml中做一些调用集合函数方法等,所有的判断是否为空,判断集合大小这种操作都放到Java代码中执行,现有已存在的代码要尽快修改测试

单元测试模拟并发环境

public class UserProductDelayTest extends Junit4Base {
    @Autowired
    private IUserProductDelayService userProductDelayService;
    //模拟短时间内的并发请求量
    private static final int threadNum = 20;
    //倒计时器,用于模拟高并发
    private CountDownLatch cdl = new CountDownLatch(threadNum);

    @Test
    public void demo() {
        for (int i = 0; i < threadNum; i++) {
            new Thread(new UserRequest()).start();
            //倒计时计数一次
            cdl.countDown();
        }
        try {
            //阻塞主线程,等待所有的子线程执行完毕
            Thread.currentThread().join();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //
    private class UserRequest implements Runnable {

        @Override
        public void run() {
            try {
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

          //此处进行写要并发测试的代码
            List<UserProductDelay> delayList =
                  userProductDelayService.findUserProductDelaysByOrderNoAndProductId("123123", 1, UserProductDelay.Status.AUDITEND);
            System.out.println(JSON.toJSONString(delayList));
        }
    }
}

利用CountDownLatch的特性实现模拟并发线程访问,建议所有的对外提供服务的service做单元测试的时候用这种方式自测一下

上一篇下一篇

猜你喜欢

热点阅读