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>
parameterType="String", this is necessary, and the parameter type must be string.
- 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.
<when test="username != null">, you have a corresponding test judgment statement, and it may be if.
- 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:
- Mybatis-3.2.3-sources.jar
- 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 ; }
- The value of expression is
username != null
- The value of Parameterobject is
{_parameter=00010001, _databaseId=null}
- 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