There is no getter for property named ' * ' in ' class java.lang.String ' source analysis

Source: Internet
Author: User

There is no getter for property named ‘*‘ in ‘class java.lang.String‘ , this error occurs because MyBatis parameterType="String" has a limitation on the SQL statement, and if you use <when test="username != null"> such a condition to judge, the error will occur, but let's look into it today .

First, false reproduction

If you want to go backwards, you need to reproduce the error, so let's say we have an SQL query:

<select id="Getriskmember" resultmap="Baseresultmap"  ParameterType="String">    <include refid="Selectmember"/>    <Choose>        < when test="Username! = null">and username = #{username}</when >        <otherwise>and Safetylevel > 1</otherwise>    </Choose> </Select>
    1. parameterType="String", this is necessary, and the parameter type must be string.
    2. The corresponding method in the Mapper class for this SQL is, that is, the List<Member> getRiskMember(String username); passed parameter is named username, and normally this is a reasonable configuration.
    3. <when test="username != null">, you have a corresponding test judgment statement, and it may be if.
    4. At this point, when the project runs the query statement, it throws an There is no getter for property named ‘username‘ in ‘class java.lang.String‘ error!
Ii. Solutions

Of course, if you do not have the time to look at the source code analysis examples, I would like to tell you the solution, lest you be troubled by the problem. The solution is very simple, you just need to <when test="username != null"> modify as <when test="_parameter!= null"> well, other places do not need to change ( and username = #{username} that is, do not need to change and username = #{_parameter} ), the modified SQL statement is as follows:

<select id="Getriskmember" resultmap="Baseresultmap"  ParameterType="String">    <include refid="Selectmember"/>    <Choose>        < when test="_parameter! = null">and username = #{username}</when >        <otherwise>and Safetylevel > 1</otherwise>    </Choose> </Select>
Third, source code analysis

Of course, if you have the time, take a look at the source code analysis, or try it yourself, I believe you will be a big success!

①, prepare the source package

You need such two files, specifically how to download I will not say more, if you need, you can add group 120926808:

    1. Mybatis-3.2.3-sources.jar
    2. Mybatis-spring-1.2.2-sources.jar

Of course, the corresponding LIB package in your project is also the corresponding version.

Then, we put the corresponding source code to decompile, generate the corresponding source, using the tool is Jd-gui.exe.

Next, let's look at how to correlate the source package, see:

I have loaded well, if it is the first time, click Edit, in the pop-up box, select the previous step to save the zip file.

②, test Cases

Prepare the source package, we write a test case, the direct main method can be, of course, the project is different, the method is naturally different, simple as follows:

publicstaticvoidmainthrows IOException {    SpringUtils.getSpringContext();    MemberMapper mapper = SpringUtils.getBeansByClassType(MemberMapper.class);    mapper.getRiskMember("00010001");}

We'll mapper.getRiskMember("00010001"); make a break in this line.

③, Debug Debug

Run the Main method directly, F5 at the breakpoint, and enter into the Mapperproxy.java

publicinvokethrows Throwable {  if (Object.class.equals(method.getDeclaringClass())) {    return method.invoke(this, args);  }  final MapperMethod mapperMethod = cachedMapperMethod(method);  return mapperMethod.execute(sqlSession, args); }

Can follow the debug into the Mappermethod.java

  Private<E> ObjectExecuteformany(Sqlsession sqlsession, object[] args)    {list<e> result; Object param = Method.convertargstosqlcommandparam (args);if(Method.hasrowbounds ())      {Rowbounds rowbounds = method.extractrowbounds (args);    result = Sqlsession.<e>selectlist (Command.getname (), param, rowbounds); }Else{result = Sqlsession.<e>selectlist (Command.getname (), param); }//Issue #510 Collections & Arrays Support    if(!method.getreturntype (). IsAssignableFrom (Result.getclass ())) {if(Method.getreturntype (). IsArray ()) {returnConverttoarray (result); }Else{returnConverttodeclaredcollection (Sqlsession.getconfiguration (), result); }    }returnResult }

After entering the method, you can always debug to result = sqlSession.<E>selectList(command.getName(), param); that line of code. At this point, you need to hold down the CTRL key while clicking the left mouse button, see:

Select Open implementation in the pop-up box and go to Defaultsqlsession.java

  publicselectList(String statement, Object parameter) {    returnthis.selectList(statement, parameter, RowBounds.DEFAULT);  }

Hit a breakpoint on the return this.selectList line, and then press F8 shortcut key to go to the method to continue debugging, (confined to space, omit steps, use in subsequent articles ...) instead), until you enter into the Cachingexecutor.java

publicquerythrows SQLException {   BoundSql boundSql = ms.getBoundSql(parameterObject);   CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);   return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

Tips: The trick is in the BoundSql boundSql = ms.getBoundSql(parameterObject); process of executing this line of code.

(... (Omit step, please note during personal debugging.) )

Until you get into the Dynamiccontext.java class,

  publicDynamicContext(Configuration configuration, Object parameterObject) {    ifnullinstanceof Map)) {      MetaObject metaObject = configuration.newMetaObject(parameterObject);      new ContextMap(metaObject);    else {      new ContextMap(null);    }    bindings.put(PARAMETER_OBJECT_KEY, parameterObject);    bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());  }

At this point, you might as well wait for a moment and look at the whole code of the class, and you'll find:

  publicstaticfinal"_parameter";  publicstaticfinal"_databaseId";

There are two constants here, of course, but look here, maybe you'll find "_parameter" this keyword, but it doesn't explain anything at this point, you remember bindings.put(PARAMETER_OBJECT_KEY, parameterObject); , and you ContextMap bindings have a little bit of an impression of the object.

key1: _parameter

(... (Omit step, please note during personal debugging.) )

Then we enter the Mixedsqlnode.java

  publicbooleanapply(DynamicContext context) {    for (SqlNode sqlNode : contents) {      sqlNode.apply(context);    }    returntrue;  }

The Apply method is very interesting, XML configuration of SQL statements, the method will be converted to standard SQL (called the Standard, is the value of the formation of the SQL statement is able to execute a preprocessing SQL query string), you might as well slow execute the loop statement.

The second cycle, you can see the embryonic form of SQL, then please continue.

(... (Omit step, please note during personal debugging.) )

Until you find out thatthe type of Sqlnode is Choosesqlnode, at this point, you can relate to the following:

<choose>        <when test="_parameter != null">

It's good to begin to become clear.

(... (Omit step, please note during personal debugging.) )

Continue debugging until you get to Expressionevaluator.java.

 public  boolean  evaluateboolean  (String expression, Object    Parameterobject) {Object value = ognlcache.getvalue (expression, parameterobject); if  (Value instanceof  Boolean) return     (Boolean) value; if  (Value instanceof  number) return ! new     BigDecimal (string.valueof (value)). Equals (Bigdecimal.zero);  return  value! = null ; }
    1. The value of expression isusername != null
    2. The value of Parameterobject is{_parameter=00010001, _databaseId=null}
    3. There seems to be a relationship between the above two parameters, but it's still a few steps away from the source.

Immediately thereafter, we entered into the Ognlcache.java

  publicstaticgetValue(String expression, Object root) {    try {      return Ognl.getValue(parseExpression(expression), root);    catch (OgnlException e) {      thrownew BuilderException("Error evaluating expression ‘""‘. Cause: " + e, e);    }  }

Enter to Ognlcache.java

  Private StaticObjectparseexpression(String expression)throwsognlexception {Try{Node node = expressioncache.get (expression);if(node = =NULL) {node =NewOgnlparser (NewStringReader (expression)). Toplevelexpression ();      Expressioncache.put (expression, node); }returnNode }Catch(ParseException e) {Throw NewExpressionsyntaxexception (expression, e); }Catch(Tokenmgrerror e) {Throw NewExpressionsyntaxexception (expression, e); }  }

Key2:
1. parseExpression(expression) the type is node and its value is username != null .
2. Root is of type DynamicContext$ContextMap (id=41) , and its value is{_parameter=00010001, _databaseId=null}

(... (Omit step, please note during personal debugging.) )

When it goes on, it's back to Defaultsqlsession.java.

  publicselectList(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;    catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    finally {      ErrorContext.instance().reset();    }  }

Now that the error has been thrown, see

To this, the exception is found how to throw out, but the overall look up, it seems that the shortcomings of what, yes, because eclipse can no longer see Ognl.getValue(parseExpression(expression), root); , so it will cause trouble, we can see the GetValue method through the Anti-compilation tool.

  publicstaticgetValue(Object tree, Object root)    throws OgnlException  {    returnnull);  }
  publicstaticgetValue(Object tree, Map context, Object root)    throws OgnlException  {    returnnull);  }
 public  static  object getvalue  (object tree, Map context, object root, Class resulttype) throws  ognlexception {ognlcontext ognlcontext = (OgnlContext) A    Dddefaultcontext (root, context);    Object result = ((Node) tree). GetValue (Ognlcontext, Root); if  (Resulttype! = null ) {result = Gettypeconverter (context). Convertvalue (context, root, null , null , result, resulttype);  } return  result; }

At this point, we can know that it is impossible to match the value of the Porperty to the Key2, {_parameter=00010001, _databaseId=null} username so the program throws an org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘username‘ in ‘class java.lang.String‘ error!

Should not lay down the heavy shell, looking for where the sky is blue-Jay Chou "snail"
This article is from: "Silent Wang er Blog"

There is no getter for property named ' * ' in ' class java.lang.String ' source analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.