iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >Mybatis中Size()方法的作用是什么
  • 481
分享到

Mybatis中Size()方法的作用是什么

2024-04-02 19:04:59 481人浏览 独家记忆
摘要

这篇文章将为大家详细讲解有关mybatis中Size()方法的作用是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。前言MyBatis 是一个开源的轻量级

这篇文章将为大家详细讲解有关mybatis中Size()方法的作用是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

前言

MyBatis 是一个开源的轻量级的半自动化ORM 框架,用于面向对象和关系型数据库的映射,其中 xml  文件,和sql语句结合,最大的特点,应用程序sql解耦。OGNL表达式,是MyBatis中的广泛应用,是一种EL语言,用于设置和获取 Java  对象的属性,并且可以对列表进行投影和执行lambda表达式,ognl提供了简单,便于执行的ognl表达式。一个线上服务,经常会出现一个异常,构造各种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>

线程下进行压力测试

String resource = "mybatis-config.xml";         InputStream in = null;         try {             in = Resources.getResourceAsStream(resource);             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);             final List<Long> ids = Collections.singletonList(1L);             final SqlSession session = sqlSessionFactory.openSession();             final CountDownLatch mCountDownLatch = new CountDownLatch(1);             for (int i = 0; i < 50; i++) {                 Thread thread = new Thread(new Runnable() {                     public void run() {                         try {                             mCountDownLatch.await();                         } catch (InterruptedException e) {                             e.printStackTrace();                         }                         for (int k = 0; k < 100; k++) {                             session.selectList("CompanyMapper.getCompanysByIds", ids);                         }                     }                 });                 thread.start();             }             mCountDownLatch.countDown();             synchronized (MybatisBugTest.class) {                 try {                     MybatisBugTest.class.wait();                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }          } catch (IOException e) {             e.printStackTrace();         } catch (Throwable e) {             e.printStackTrace();         } finally {             if (in != null)                 try {                     in.close();                 } catch (IOException e) {                     e.printStackTrace();                 }         }

上述代码在并发的时候会出现异常。

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)

异常信息表明ognlRuntime类不能访问

查看源码,破案

java.util.Collections的私有成员SingletonList。查看源代码,可以知道定在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);     }

其方法代码

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

问题出现在meta是一个共享变量,即

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

当,第一个线程t1到第一行代码允许method方法可以调用,第二个线程t2,执行到2把方法method设置为不可访问,接着t1又执行,此时行列3会发生异常。

升级版本

lgnl2.7,已经修复了这个问题,所以修复后的代码如下

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

关于Mybatis中Size()方法的作用是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

您可能感兴趣的文档:

--结束END--

本文标题: Mybatis中Size()方法的作用是什么

本文链接: https://www.lsjlt.com/news/57829.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • sql中外码怎么设置
    sql 中外码设置步骤:确定父表和子表。在子表中创建外码列,引用父表主键。使用 foreign key 约束将外码列链接到父表主键。指定引用动作,以处理父表数据更改时的子表数据操作。 ...
    99+
    2024-05-15
  • sql中having是什么
    having 子句用于过滤分组结果,应用于分组后的数据集。它与 where 子句类似,但基于分组结果而不是原始数据。用法:1. 过滤分组后的聚合值。2. 根据分组后的...
    99+
    2024-05-15
  • 在sql中空值用什么表示
    在 sql 中,空值表示未知或不存在的值,可使用 null、空字符串或特殊值表示。处理空值的方法包括使用操作符(is null/is not null)、coalesce 函数(返回第一...
    99+
    2024-05-15
    oracle
  • sql中number什么意思
    sql 中的 number 类型用于存储数值数据,包括小数和整数,特别适合货币、度量和科学数据。其精度由 scale(小数点位数)和 precision(整数字段和小数字段总位数)决定。...
    99+
    2024-05-15
  • sql中空值赋值为0怎么写
    可以通过使用 coalesce() 函数将 sql 中的空值替换为指定值(如 0)。coalesce() 的语法为 coalesce(expression, replacement),其...
    99+
    2024-05-15
  • sql中revoke语句的功能
    revoke 语句用于撤销指定用户或角色的权限或角色成员资格。可撤销的权限包括 select、insert、update、delete 等,撤销的对象类型包括表、视图、存储过程...
    99+
    2024-05-15
    敏感数据
  • sql中REVOKE是什么意思
    revoke 是 sql 中用于撤销用户或角色对数据库对象权限的命令。它通过撤销权限类型、对象级别和目标权限来实现:权限类型:撤销 select、insert、update、d...
    99+
    2024-05-15
  • sql中sp是什么意思
    sql中的sp是存储过程的缩写,它是一种预编译的、已命名的sql语句块,存储在数据库中,可以被用户通过简单命令调用。存储过程的特点有:可重用性、模块化、性能优化、安全性、事务支持。存储过...
    99+
    2024-05-15
    敏感数据
  • sql中references是什么意思
    sql 中的 references 关键字用于在外键约束中定义表之间的父-子关系。外键约束确保子表中的行都引用父表中存在的行,从而维护数据完整性。references 语法的格式为:fo...
    99+
    2024-05-15
  • sql中判断字段为空怎么写
    sql 中可通过 4 种方法判断字段是否为空:1)is null 运算符;2)is not null 运算符;3)coalesce() 函数;4)case 语句。例如,查询所有 colu...
    99+
    2024-05-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作