关于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做单元测试的时候用这种方式自测一下
网友评论