欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis 中如何判斷集合的size

 更新時(shí)間:2021年02月03日 16:56:49   作者:大樹葉  
這篇文章主要介紹了在Mybatis中判斷集合的size操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

Mybatis中判斷集合的size,可以用下面的方法來(lái)做。

<if test="null != staffCodeList and staffCodeList.size > 0">
and gui.USER_CODE not in
<foreach collection="staffCodeList" item="staffCode" open="(" separator="," close=")">
#{staffCode}
</foreach>
</if>

補(bǔ)充:警惕,MyBatis的size()方法竟然有坑!

Mybatis是一個(gè)開源的輕量級(jí)半自動(dòng)化ORM框架,使得面向?qū)ο髴?yīng)用程序與關(guān)系數(shù)據(jù)庫(kù)的映射變得更加容易。

MyBatis使用xml描述符或注解將對(duì)象與存儲(chǔ)過(guò)程或SQL語(yǔ)句相結(jié)合。 Mybatis最大優(yōu)點(diǎn)是應(yīng)用程序與Sql進(jìn)行解耦,sql語(yǔ)句是寫在Xml Mapper文件中。

OGNL表達(dá)式在Mybatis當(dāng)中應(yīng)用非常廣泛,其表達(dá)式的靈活性使得動(dòng)態(tài)Sql功能的非常強(qiáng)大。

OGNL是Object-Graph Navigation Language的縮寫,代表對(duì)象圖導(dǎo)航語(yǔ)言。

OGNL是一種EL表達(dá)式語(yǔ)言,用于設(shè)置和獲取Java對(duì)象的屬性,并且可以對(duì)列表進(jìn)行投影選擇以及執(zhí)行l(wèi)ambda表達(dá)式。

Ognl類提供了許多簡(jiǎn)便方法用于執(zhí)行表達(dá)式的。 Struts2發(fā)布的每個(gè)版本都會(huì)出現(xiàn)的新的高??蓤?zhí)行漏洞也是因?yàn)樗褂昧遂`活的OGNL表達(dá)式。

公司后端采用Mybatis作為數(shù)據(jù)訪問(wèn)層,所使用版本為3.2.3。

線上環(huán)境業(yè)務(wù)系統(tǒng)在運(yùn)行過(guò)程中出現(xiàn)了一個(gè)令人困惑的異常, 該異常時(shí)而出現(xiàn)時(shí)而不出現(xiàn),構(gòu)造各種OGNL表達(dá)式為空等特殊情況均不會(huì)重現(xiàn)該異常。

具體異常堆棧信息如下:

### Error querying database. Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"]
### Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"]
 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23) org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98)
 at cn.com.shaobingmm.MybatisBugTest$2.run(MybatisBugTest.java:88)
 at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"]
 at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java
 at:47)
 at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:29)
 at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:30)
 at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)
 at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:51)
 at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)
 at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:37)
 at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:275)
 at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:79)
 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)
 ... 3 more
Caused by: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"]
 at org.apache.ibatis.ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:837)
 at org.apache.ibatis.ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:61)
 at org.apache.ibatis.ognl.OgnlRuntime.callMethod(OgnlRuntime.java:860)
 at org.apache.ibatis.ognl.ASTMethod.getValueBody(ASTMethod.java:73)
 at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
 at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
 at org.apache.ibatis.ognl.ASTChain.getValueBody(ASTChain.java:109)
 at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
 at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
 at org.apache.ibatis.ognl.ASTGreater.getValueBody(ASTGreater.java:49)
 at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
 at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
 at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:56)
 at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
 at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
 at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:333)
 at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:413)
 at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:395)
 at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:45)
 ... 12 more

List的size()方法明顯是public為何還會(huì)出現(xiàn)不可訪問(wèn)的異常。該問(wèn)題并不是每一次都會(huì)出現(xiàn),經(jīng)過(guò)多次嘗試,該異常一直未在測(cè)試環(huán)境重現(xiàn)。

該接口在完整調(diào)用鏈路中的出錯(cuò)次數(shù)占總調(diào)用次數(shù)的比率為0.01%,無(wú)意中聯(lián)想到并發(fā)問(wèn)題在周期性時(shí)間內(nèi)往往是概率性發(fā)生。

編寫模擬多線程環(huán)境并發(fā)讀取公司列表測(cè)試代碼:

<mapper namespace="CompanyMapper">
 <select id="getCompanysByIds"resultType="cn.com.shaobingmm.Company">
  select *
  from company
  <where>
   <if test="list != null and list.size() > 0">
    and id in
  <foreach collection="list" item="id" open="(" separator="," close=")">#{id}
</foreach>
   </if>
  </where>
 </select>
</mapper>

多線程并發(fā)環(huán)境下的壓測(cè)代碼

上訴異常堆棧信息在并發(fā)環(huán)境下果然重現(xiàn)出現(xiàn),根據(jù)異常信息代碼執(zhí)行至該行代碼時(shí)發(fā)生異常:

異常信息表明OgnlRuntime類不能夠訪問(wèn)java.util.Collections的私有成員SingletonList。

查看源代碼發(fā)現(xiàn)能夠拋出MethodFailedException異常可以鎖定在invokeMethod方法內(nèi)部。

public static Object callAppropriateMethod(OgnlContext context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args) throws MethodFailedException {
  Object reason = null;
  Object[] actualArgs = objectArrayPool.create(args.length);
 
  try {
   Method e = getAppropriateMethod(context, source, target, methodName, propertyName, methods, args, actualArgs);
   if(e == null || !isMethodAccessible(context, source, e, propertyName)) {
    StringBuffer buffer = new StringBuffer();
    if(args != null) {
     int i = 0;
 
     for(int ilast = args.length - 1; i <= ilast; ++i) {
      Object arg = args[i];
      buffer.append(arg == null?NULL_STRING:arg.getClass().getName());
      if(i < ilast) {
       buffer.append(", ");
      }
     }
    }
 
    throw new NoSuchMethodException(methodName + "(" + buffer + ")");
   }
 
   Object var14 = invokeMethod(target, e, actualArgs);
   return var14;
  } catch (NoSuchMethodException var21) {
   reason = var21;
  } catch (IllegalAccessException var22) {
   reason = var22;
  } catch (InvocationTargetException var23) {
   reason = var23.getTargetException();
  } finally {
   objectArrayPool.recycle(actualArgs);
  }
 
  throw new MethodFailedException(source, methodName, (Throwable)reason);
 }

invokeMethod方法代碼

public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException {
  boolean wasAccessible = true;
  if(securityManager != null) {
   try {
    securityManager.checkPermission(getPermission(method));
   } catch (SecurityException var6) {
    throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
   }
  }
 
  if((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !(wasAccessible = method.isAccessible())) {
   method.setAccessible(true); (1)
  }
 
  Object result = method.invoke(target, argsArray); (3)
  if(!wasAccessible) {
   method.setAccessible(false); (2)
  }
 
  return result;
 }

問(wèn)題出現(xiàn)在method實(shí)際上是一個(gè)共享變量,也就是例子中的

public int java.util.Collections$SingletonList.size()

方法

當(dāng)?shù)谝粋€(gè)線程t1至(1)行代碼允許method方法可以被調(diào)用,第二個(gè)線程t2執(zhí)行至(2)將method的方法設(shè)置為不可以訪問(wèn)。接著t1又開始執(zhí)行到(3)行的時(shí)候就會(huì)發(fā)生該異常。這是一個(gè)很典型的同步問(wèn)題。

Ognl2.7已經(jīng)修復(fù)了該問(wèn)題,因?yàn)閛gnl源碼是直接打包內(nèi)嵌在mybatis包中,mybatis3.3.0版本中也已經(jīng)進(jìn)行了修復(fù)升級(jí)。(劃重點(diǎn))

public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException {
  boolean syncInvoke = false;
  boolean checkPermission = false;
  int mHash = method.hashCode();
  synchronized(method) {
   if(_methodAccessCache.get(Integer.valueOf(mHash)) == null || _methodAccessCache.get(Integer.valueOf(mHash)) == Boolean.TRUE) {
    syncInvoke = true;
   }
 
   if(_securityManager != null && _methodPermCache.get(Integer.valueOf(mHash)) == null || _methodPermCache.get(Integer.valueOf(mHash)) == Boolean.FALSE) {
    checkPermission = true;
   }
  }
 
  boolean wasAccessible = true;
  Object result;
  if(syncInvoke) {
   synchronized(method) {
    if(checkPermission) {
     try {
      _securityManager.checkPermission(getPermission(method));
      _methodPermCache.put(Integer.valueOf(mHash), Boolean.TRUE);
     } catch (SecurityException var12) {
      _methodPermCache.put(Integer.valueOf(mHash), Boolean.FALSE);
      throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
     }
    }
 
    if(Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.FALSE);
    } else if(!(wasAccessible = method.isAccessible())) {
     method.setAccessible(true);
     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.TRUE);
    } else {
     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.FALSE);
    }
 
    result = method.invoke(target, argsArray);
    if(!wasAccessible) {
     method.setAccessible(false);
    }
   }
  } else {
   if(checkPermission) {
    try {
     _securityManager.checkPermission(getPermission(method));
     _methodPermCache.put(Integer.valueOf(mHash), Boolean.TRUE);
    } catch (SecurityException var11) {
     _methodPermCache.put(Integer.valueOf(mHash), Boolean.FALSE);
     throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
    }
   }
 
   result = method.invoke(target, argsArray);
  }
 
  return result;
 }

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • MyBatis實(shí)現(xiàn)配置加載的步驟

    MyBatis實(shí)現(xiàn)配置加載的步驟

    本文主要介紹了MyBatis實(shí)現(xiàn)配置加載的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Springboot教程之如何設(shè)置springboot熱重啟

    Springboot教程之如何設(shè)置springboot熱重啟

    這篇文章主要介紹了Springboot教程之如何設(shè)置springboot熱重啟,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 淺談java繼承中是否創(chuàng)建父類對(duì)象

    淺談java繼承中是否創(chuàng)建父類對(duì)象

    下面小編就為大家?guī)?lái)一篇淺談java繼承中是否創(chuàng)建父類對(duì)象。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法

    Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法

    這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • java線程并發(fā)cyclicbarrier類使用示例

    java線程并發(fā)cyclicbarrier類使用示例

    CyclicBarrier類似于CountDownLatch也是個(gè)計(jì)數(shù)器,不同的是CyclicBarrier數(shù)的是調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù),當(dāng)線程數(shù)達(dá)到了CyclicBarrier初始時(shí)規(guī)定的數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù),下面使用示例學(xué)習(xí)他的使用方法
    2014-01-01
  • Java返回可變引用對(duì)象問(wèn)題整理

    Java返回可變引用對(duì)象問(wèn)題整理

    在本篇文章里小編給大家分享的是關(guān)于Java返回可變引用對(duì)象問(wèn)題整理內(nèi)容,需要的朋友們可以學(xué)習(xí)下。
    2020-04-04
  • mybatis存在更新不存在新增問(wèn)題

    mybatis存在更新不存在新增問(wèn)題

    這篇文章主要介紹了mybatis存在更新不存在新增問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Spring Boot中如何使用Swagger詳解

    Spring Boot中如何使用Swagger詳解

    Swagger是一個(gè)規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化 RESTful風(fēng)格的Web服務(wù),這篇文章主要給大家介紹了關(guān)于Spring Boot中如何使用Swagger的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Spring的BeanFactoryPostProcessor接口示例代碼詳解

    Spring的BeanFactoryPostProcessor接口示例代碼詳解

    這篇文章主要介紹了Spring的BeanFactoryPostProcessor接口,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • JDK 7U15在 Windows x86平臺(tái)下的安裝方法

    JDK 7U15在 Windows x86平臺(tái)下的安裝方法

    本文給大家分享的是如何在windows平臺(tái)下安裝JDK最新版的方法,十分的簡(jiǎn)單全面,有需要的小伙伴可以參考下
    2016-05-05

最新評(píng)論