当前位置: 首页 > 编程笔记 >

Mybatis 中如何判断集合的size

殷轶
2023-03-14
本文向大家介绍Mybatis 中如何判断集合的size,包括了Mybatis 中如何判断集合的size的使用技巧和注意事项,需要的朋友参考一下

Mybatis中判断集合的size,可以用下面的方法来做。

<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>

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

Mybatis是一个开源的轻量级半自动化ORM框架,使得面向对象应用程序与关系数据库的映射变得更加容易。

MyBatis使用xml描述符或注解将对象与存储过程或SQL语句相结合。 Mybatis最大优点是应用程序与Sql进行解耦,sql语句是写在Xml Mapper文件中。

OGNL表达式在Mybatis当中应用非常广泛,其表达式的灵活性使得动态Sql功能的非常强大。

OGNL是Object-Graph Navigation Language的缩写,代表对象图导航语言。

OGNL是一种EL表达式语言,用于设置和获取Java对象的属性,并且可以对列表进行投影选择以及执行lambda表达式。

Ognl类提供了许多简便方法用于执行表达式的。 Struts2发布的每个版本都会出现的新的高危可执行漏洞也是因为它使用了灵活的OGNL表达式。

公司后端采用Mybatis作为数据访问层,所使用版本为3.2.3。

线上环境业务系统在运行过程中出现了一个令人困惑的异常, 该异常时而出现时而不出现,构造各种OGNL表达式为空等特殊情况均不会重现该异常。

具体异常堆栈信息如下:

### 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为何还会出现不可访问的异常。该问题并不是每一次都会出现,经过多次尝试,该异常一直未在测试环境重现。

该接口在完整调用链路中的出错次数占总调用次数的比率为0.01%,无意中联想到并发问题在周期性时间内往往是概率性发生。

编写模拟多线程环境并发读取公司列表测试代码:

<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>

多线程并发环境下的压测代码

上诉异常堆栈信息在并发环境下果然重现出现,根据异常信息代码执行至该行代码时发生异常:

异常信息表明OgnlRuntime类不能够访问java.util.Collections的私有成员SingletonList。

查看源代码发现能够抛出MethodFailedException异常可以锁定在invokeMethod方法内部。

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;
 }

问题出现在method实际上是一个共享变量,也就是例子中的

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

方法

当第一个线程t1至(1)行代码允许method方法可以被调用,第二个线程t2执行至(2)将method的方法设置为不可以访问。接着t1又开始执行到(3)行的时候就会发生该异常。这是一个很典型的同步问题。

Ognl2.7已经修复了该问题,因为ognl源码是直接打包内嵌在mybatis包中,mybatis3.3.0版本中也已经进行了修复升级。(划重点)

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;
 }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持小牛知识库。如有错误或未考虑完全的地方,望不吝赐教。

 类似资料:
  • 本文向大家介绍python中判断集合范围的方法小结,包括了python中判断集合范围的方法小结的使用技巧和注意事项,需要的朋友参考一下 我们在比较数值大小的时候,会使用一些比较符号来进行判断。在python集合中也有这样的比较,但有一点要注意的是,我们比较的是集合之间的包容性,而不是简单数值之间的大小比较,这点在文章的开头就进行明确,也是对于我们python初学者的提醒。 集合可以使用大于(>)、

  • sismember key member 存在返回1,0表示不存在或者key不存在

  • 本文向大家介绍C#中判断一个集合是否是另一个集合的子集的简单方法,包括了C#中判断一个集合是否是另一个集合的子集的简单方法的使用技巧和注意事项,需要的朋友参考一下 看到这个标题,我们首先想到的是循环遍历其中一个数组,判断数组中的每个元素是否都在另一个数组中出现了,以此来判断该数组是不是另一个数组的子集,但是这样做就太过复杂了,有没有简单一点的方法呢? 打个比方,有这样的两个集合: 现在需要判断sm

  • Spring Boot如何整合MyBatis? 如果在 Service 层有一些业务逻辑需要对 Mapper 层返回的数据进行进一步处理,有没有一些最佳实践来确保代码的可读性和可维护性?

  • 本文向大家介绍如何判断dpr的倍数?相关面试题,主要包含被问及如何判断dpr的倍数?时的应答技巧和注意事项,需要的朋友参考一下

  • 本文向大家介绍vue如何判断dom的class,包括了vue如何判断dom的class的使用技巧和注意事项,需要的朋友参考一下 vue点击给dom添加class然后获取含有class的dom 点击过后获取到的dom打印 加了判断为什么打印出来的dom是点击之前的dom 总结 以上所述是小编给大家介绍的vue如何判断dom的class,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复