Mybatis 有坑,千万别踩!

Posted Java技术栈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 有坑,千万别踩!相关的知识,希望对你有一定的参考价值。


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


具体异常堆栈信息如下:

### 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
<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)
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()
方法
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;
}


来源:zhuanlan.zhihu.com/p/30085658

- END -

推荐阅读:
1、
2、
3、
4、
5、
关注 Java技术栈 公众号在后台回复: Java ,可获取一份栈长整理的最新 Java 技术干货。

点击「阅读原文」带你飞~ 

以上是关于Mybatis 有坑,千万别踩!的主要内容,如果未能解决你的问题,请参考以下文章

冲刺春招 | 程序员面试失败六大雷区,千万别踩!

提薪必看 | 这5个提加薪最容易得罪人的坑,千万别踩雷

熬夜整理了一份北京牛逼互联网公司清单,找工作千万别踩雷了

为什么有人说“千万不要去小公司”?三点原因很现实,这些坑别踩

巨坑警告!去银行写代码千万别去分行

这8个摸鱼神器,千万别让你老板知道!