Mybatis if-test judgment interpretation
<if test="takeWay == '0'">】mybatis' if judgment
A single character must be written in double quotes, and it can be changed to <if test='takeWay == "1"'> or <if test="takeWay == '1'.toString() ">
Part of the code of the .xml file
<insert parameterType=""> insert cx_customer_deliverypreference <trim prefix="(" suffix=")" suffixOverrides=","> .... omitted here <if test="takeWay == '1' and workday != null "> WORKDAY, </if> .... </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> .... omitted here <if test="takeWay == '1' and workday != null "> #{workday, jdbcType=VARCHAR}, </if> .... </trim> </insert>
An error occurred at takeWay == "1", resulting in the SQL in the if judgment not being executed, and the program does not report an error, without any prompts. Remove takeWay == “1” and then execute. I can't figure it out.
Because I have written the following code, it is correct.
<if test="messageType == 'senderReceiveSuccess' "> ...... </if>
- Put <if test="takeWay == '1' and workday != null ">
- Change to <if test='takeWay == "1" and workday != null '>
- Or change it to <if test="takeWay == '1'.toString() and workday != null ">.
The reason is: mybatis is parsed with OGNL expressions. In OGNL expressions, '1' will be parsed into characters, Java is strongly typed, and char and a string will cause inequality, so the SQL in the if tag will not be parsed.
To summarize the usage method: a single character must be written into double quotes or .toString()!
When using Mybatis, it is often judged whether the attribute is empty
- POJO
private Integer status;//state,Probably0、1、2、3。
- Mapper XML
<sql> <trim prefix="where" prefixOverrides="and | or "> //...Omit other <if test="status != null and status !=''">and status = #{status}</if> <trim prefix="where" prefixOverrides="and | or "> </sql>
whenstatus
When the value is 0, the where SQL isand status = 0
It is not spliced normally, which means that the expression in the test isfalse
, resulting in an incorrect query result. However, obviously the value (Integer:0)! = null too! = ' ', should betrue
That's right.
When status is Integer and status value is 0, the if is judged to be false.
When status is 0, Mybatis will parse into an empty string.
In order to avoid this problem, I changed it to write it like this below, removing the judgment of empty characters, and solving the problem
<if test="status != null">and status = #{status}</if>
Cause analysis
Through Debug MyBatis source code, I found itIfSqlNode
Class, which is used to handle dynamic SQL <if> nodes, methodspublic boolean apply(DynamicContext context)
Used to construct SQL statements within nodes.
if ((test, ())
This code is parsing
<if test="status !=null and status !=''">
The key to expressions in test, if the expression is true, then splice SQL, otherwise ignore.
public class IfSqlNode implements SqlNode { private ExpressionEvaluator evaluator; private String test; private SqlNode contents; public IfSqlNode(SqlNode contents, String test) { = test; = contents; = new ExpressionEvaluator(); } public boolean apply(DynamicContext context) { if ((test, ())) { (context); return true; } return false; } }
Open the ExpressionEvaluator class and find that the parsing expression uses OGNL. If you have used the ancient Struts framework, you should be familiar with it.
pass
(expression, parameterObject);
You can see that the value of the expression is obtained from the cache. From this, we can see that MyBatis actually caches the expression to improve performance.
public class ExpressionEvaluator { public boolean evaluateBoolean(String expression, Object parameterObject) { Object value = (expression, parameterObject); if (value instanceof Boolean) return (Boolean) value; if (value instanceof Number) return !new BigDecimal((value)).equals(); return value != null; }
Follow in and finally find a way to parse the expression
private static Object parseExpression(String expression)
This method will first retrieve the value from the cache, and if it does not, it will be parsed and put into the cache, and then call
(parseExpression(expression), root)
Get the value of the expression.
public class OgnlCache { private static final Map<String, > expressionCache = new ConcurrentHashMap<String, >(); public static Object getValue(String expression, Object root) throws OgnlException { return (parseExpression(expression), root); } private static Object parseExpression(String expression) throws OgnlException { try { Node node = (expression); if (node == null) { node = new OgnlParser(new StringReader(expression)).topLevelExpression(); (expression, node); } return node; } catch (ParseException e) { throw new ExpressionSyntaxException(expression, e); } catch (TokenMgrError e) { throw new ExpressionSyntaxException(expression, e); } }
As for
(parseExpression(expression), root)
How it works? If you are interested, you can follow it and find out on your own. I won’t go into details in this article.
So far, we already know that the expressions of MyBatis are processed in OGNL, which is enough.
Let’s go to the OGNL official website to see if there is a problem with our expression syntax, which leads to the occurrence of this problem.
Interpreting Objects as Booleans
Any object can be used where a boolean is required. OGNL interprets objects as booleans like this:
- If the object is a Boolean, its value is extracted and returned;
- If the object is a Number, its double-precision floating-point value is compared with zero; non-zero is treated as true, zero as false;
- If the object is a Character, its boolean value is true if and only if its char value is non-zero;
- Otherwise, its boolean value is true if and only if it is non-null.
Sure enough, if the object is of a Number type, the value is 0 will be parsed asfalse
, otherwisetrue
, the same is true for floating point type 0.00.
OGNL's definition of boolean is a bit similar to JavaScript, that is,'' == 0 == false
。
This is not difficult to understand
<if test="status != null and status !=''">and status = #{status}</if>
The problem occurs when status=0, obviously0!=''
is not true, resulting in the value of the expression being false.
Modify the expression to
<if test="status != null">and status = #{status}</if>
This problem was solved.
ShouldThe root of the problem is still the irregularity of encoding, only the String type needs to be judged whether it is!=''
, other types do not have this need at all. It may be that the developer directly copied the previous line and took it to change it in order to save trouble, or the MyBatis generation tool used is not rigorous, which leads to the occurrence of this problem.
It is necessary to mention another "pit" here, if you have similarString str ="A";
<if test="str!= null and str == 'A'">
You have to be careful when writing this way.
Because if the single quote is a single character, OGNL will be recognized as the char type in Java, obviously the String type and the char type do==
The operation will returnfalse
, resulting in the expression not being true.
The solution is very simple, modify it to<if test='str!= null and str == "A"'>
Just do it.
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.