關(guān)于mybatis的xml標(biāo)簽使用問題和單元測試模擬高并發(fā)場景
標(biāo)簽使用問題
線上問題復(fù)現(xiàn)
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)
查詢對應(yīng)的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>
問題就出現(xiàn)在list.size()這上面
目前的mybatis版本為3.2.8,框架對于傳入的集合的調(diào)用size方法這部分并發(fā)控制的并不好,導(dǎo)致并發(fā)量稍微一高就會導(dǎo)致拋出異常,這里有一個關(guān)于這個問題的說明
https://zhuanlan.zhihu.com/p/30085658
此處建議:
不要在mybatis的xml中做一些調(diào)用集合函數(shù)方法等,所有的判斷是否為空,判斷集合大小這種操作都放到Java代碼中執(zhí)行,現(xiàn)有已存在的代碼要盡快修改測試
單元測試模擬并發(fā)環(huán)境
public class UserProductDelayTest extends Junit4Base {
@Autowired
private IUserProductDelayService userProductDelayService;
//模擬短時間內(nèi)的并發(fā)請求量
private static final int threadNum = 20;
//倒計時器,用于模擬高并發(fā)
private CountDownLatch cdl = new CountDownLatch(threadNum);
@Test
public void demo() {
for (int i = 0; i < threadNum; i++) {
new Thread(new UserRequest()).start();
//倒計時計數(shù)一次
cdl.countDown();
}
try {
//阻塞主線程,等待所有的子線程執(zhí)行完畢
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();
}
//此處進行寫要并發(fā)測試的代碼
List<UserProductDelay> delayList =
userProductDelayService.findUserProductDelaysByOrderNoAndProductId("123123", 1, UserProductDelay.Status.AUDITEND);
System.out.println(JSON.toJSONString(delayList));
}
}
}
利用CountDownLatch的特性實現(xiàn)模擬并發(fā)線程訪問,建議所有的對外提供服務(wù)的service做單元測試的時候用這種方式自測一下