mybatis-3: Getting "NoSuchPropertyException" Randomly

Hi,

We are using mybatis-3.3.0. Recently we got one strange error from mybatis code while executing a query that gets executed successfully many times during the day, but failed several times.

Below is the stacktrace:

org.apache.ibatis.ognl.NoSuchPropertyException: OrderXEO.cinemaAddress
    at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:55) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:280) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:80) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:120) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:73) ~[mybatis-3.3.0.jar:3.3.0]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_45]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_45]
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ~[mybatis-spring-1.2.2.jar:1.2.2]
    ... 36 more
Caused by: org.apache.ibatis.ognl.NoSuchPropertyException: OrderXEO.cinemaAddress
    at org.apache.ibatis.ognl.ObjectPropertyAccessor.getProperty(ObjectPropertyAccessor.java:151) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:2434) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.ASTProperty.getValueBody(ASTProperty.java:114) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.ASTChain.getValueBody(ASTChain.java:141) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:50) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:61) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:494) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:458) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:44) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:55) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:280) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:80) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:120) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113) ~[mybatis-3.3.0.jar:3.3.0]
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:73) ~[mybatis-3.3.0.jar:3.3.0]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_45]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_45]
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ~[mybatis-spring-1.2.2.jar:1.2.2]

Any help to find root cause of above error will be greatly appreciated. Ps. We have met a similar problem(https://github.com/mybatis/mybatis-3/issues/199) when using mybatis-3.1.0, which has fixed in mybatis-3.3.0. Is this the same cause?

MyBatis version

3.3.1

Database vendor and version

Test case or example project

Steps to reproduce

Expected result

Actual result

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 37 (17 by maintainers)

Most upvoted comments

@salten ,

Thank you for the follow-up!

MyBatis 3.3.1 (and 3.4.0) includes OGNL 3.1.2. I didn’t realize it, but in OGNL 3.1.2, getReadMethod() always returns null (because wrong parameter is specified in getMethodValue()) as you pointed out. So, you are right that this exception occurs in MyBatis 3.3.1 or 3.4.0 under heavy load even on Java 8.

In your case, upgrading MyBatis to 3.4.2 may fix the problem because this problem does not exist in the included OGNL [1] even though the race condition in getGetMethod() still exists. If you have to use 3.3.1, the interceptor solution might be a good workaround.

[1] Note that there still is another getReadMethod() issue with synthetic methods which causes the same exception on Java 6 and 7.

p.s. He didn’t mention, but it would make sense if @arebya specified an older version of OGNL .jar when he ran the demo app on Java 8u112.

@harawata I don’t know what version you used. In my mybatis3.3.1 version, the source which I decompile is :

/*      */   public static final Object getMethodValue(OgnlContext context, Object target, String propertyName, boolean checkAccessAndExistence)
/*      */     throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
/*      */   {
/* 1444 */     Object result = null;
/* 1445 */     Method m = getGetMethod(context, (target == null) ? null : target.getClass(), propertyName);
/* 1446 */     if (m == null) {
/* 1447 */       m = getReadMethod((target == null) ? null : target.getClass(), propertyName, 0);
/*      */     }
/* 1449 */     if ((checkAccessAndExistence) && ((
/*      */
/* 1451 */       (m == null) || (!(context.getMemberAccess().isAccessible(context, target, m, propertyName))))))
/*      */     {
/* 1453 */       result = NotFound;
/*      */     }
/*      */ 
/* 1456 */     if (result == null) {
/* 1457 */       if (m != null)
/*      */         try {
/* 1459 */           result = invokeMethod(target, m, NoArguments);
/*      */         } catch (InvocationTargetException ex) {
/* 1461 */           throw new OgnlException(propertyName, ex.getTargetException());
/*      */         }
/*      */       else {
/* 1464 */         throw new NoSuchMethodException(propertyName);
/*      */       }
/*      */     }
/* 1467 */     return result;
/*      */   }

The getReadMethod is :m = getReadMethod((target == null) ? null : target.getClass(), propertyName, 0); There is not the method which you write:

public static final Object getMethodValue(OgnlContext context, Object target, String propertyName, boolean checkAccessAndExistence) throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
{
  Object result = null;
  Method m = getGetMethod(context, (target == null) ? null : target.getClass() , propertyName);
  if (m == null)
    m = getReadMethod((target == null) ? null : target.getClass(), propertyName, null);
  ...

And the method “getReadMethod” is always return null…

import java.lang.reflect.Method;

import org.apache.ibatis.ognl.OgnlRuntime;

public class NoSuchPropertyExceptionTest {

	public static void main(String[] args) {
		Method m = OgnlRuntime.getReadMethod(People.class, "name", 0);
		if (m != null) {
			System.out.println(m.getName() + " had got!!");
		} else {
			System.out.println("null had got!!");
			System.exit(1);
		}
	}
}

class People {

	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Maybe your source version is different to mine…

@infear-on-the-way @rwtq2ksw , Are your getter methods override methods of super class/interface with different return types?

This could happen if the getter method generates a synthetic method, in theory.

That exception is thrown if this line is executed. It happens when both OgnlRuntime#getGetMethod() and OgnlRuntime#getReadMethod() return null.

OgnlRuntime#getGetMethod() could return null when multiple threads reach this line simultaneously. After the first thread put an entry to the cache at this line, the other threads return null from this line.

And OgnlRuntime#getReadMethod() could return null when the getter generates a synthetic (a.k.a. bridge) method [1]. Due to this JDK bug, java.beans.BeanInfo#getMethodDescriptors() could return synthetic version of the method and isMethodCallable() returns false in that case.

[1] It basically means that the method has a different return type than its super class or interface. In the following examples, the getId() methods generate synthetic methods.

public interface HasId<T> {
  public T getId();
}

public class Person implements HasId<Integer> {
  public Integer getId() {
    return id;
  }
  // ...
}
public class Parent {
  private Number id;
  public Number getId() {
    return id;
  }
}

public class Child extends Parent {
  public Integer getId() {
    return id;
  }
  // ...
}

@harawata Thank you for the answer. I have to use 3.3.1, and I added an interceptor to fix this ploblem…

I add a interceptor of mybatis to fix this problem. when the NoSuchPropertyException occurs, I retry once.

Yes, all properties have public getter methods. I tried debug the code, but i can’t reproduce the bug during debugging. Only fail when multiple requests run at the same time before server start. The same requests, launch sequentially, or before the first request response, run correctly.