Mybatis parsing expression source code analysis

The following example is often used in Mybatis development process

The Mapper is as follows

Map<String ,String > testArray(@Param("array") String [] array);

Copy the code

The SQL in XMl is as follows

<select id="testArray" resultType="map">
    select * from t_ams_ac_pmt_dtl where  cpt_pro=#{cptProp}
    <if test="array!=null and array != '' ">
        and cpt_pro=#{cptProp}
    </if>
</select>

Copy the code

How can an array be compared to an empty string? At first you might think that this code is absolutely wrong when it runs, but when you write a unit test, you run it and it works. Mybatis encapsulates array type data internally. So there is this source code parsing tour. Mybatis parse using OGNL. As for what OGNL copied a paragraph in Baidu Encyclopedia

OGNL is the abbreviation of Object-Graph Navigation Language. It is a powerful expression Language. Through its simple and consistent expression syntax, it can access any attribute of the Object, call the method of the Object, traverse the structure diagram of the whole Object, and achieve field type transformation. It uses the same expression to access attributes of the object. So we can get better data.

The unit test classes are as follows

    @Test
    public void testArray(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        TBapCheckPtsTranscdMapper mapper = sqlSession.getMapper(TBapCheckPtsTranscdMapper.class);
        String str= "1, 2, 3";
        String [] strings = str.split(",");
        mapper.testArray(strings);
    }

Copy the code

First let’s take a look at the DynamicSqlSource class, which has a method like this

@Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<? > parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject);for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

Copy the code

Among them

rootSqlNode.apply(context);

Copy the code

This code dynamically concatenates the SQL, and then click on it to take a look

 @Override
  public boolean apply(DynamicContext context) {
    for (SqlNode sqlNode : contents) {
      sqlNode.apply(context);
    }
    return true;
  }

Copy the code

Different SQLNodes call different methods, but the end result is the same: concatenated SQL. For example, the first time we go into apply it jumps to the StaticTextSqlNode class and calls the following method

  @Override
  public boolean apply(DynamicContext context) {
    context.appendSql(text);
    return true;
  }

Copy the code

Concatenate the SQL directly to

select * from t_ams_ac_pmt_dtl where  cpt_pro=#{cptProp}

Copy the code

Then the second loop finds that it jumps to IfSqlNode, which is a judgment class labeled

,

  @Override
  public boolean apply(DynamicContext context) {
    if (evaluator.evaluateBoolean(test, context.getBindings())) {
      contents.apply(context);
      return true;
    }
    return false;
  }

Copy the code

Two parameters are passed in the parse statement

evaluator.evaluateBoolean(test, context.getBindings())

Copy the code
  • test: is the expression to be parsed, in this casearray! =null and array ! = ' '
  • context.getBindings(): Gets a Map where the parameters are storedarrayThe corresponding value of, as shown below

Then came OGNL parsing expressions, and found that it ended up in things like ASTNotEq

    protected Object getValueBody(OgnlContext context, Object source) throws OgnlException {
        Object v1 = this._children[0].getValue(context, source);
        Object v2 = this._children[1].getValue(context, source);
        return OgnlOps.equal(v1, v2) ? Boolean.FALSE : Boolean.TRUE;
    }
Copy the code

Here the parsing is divided into two steps, the above expression is array! =null and array ! = “then he will group them by and and put them in the Node array.

  • Node[0]:array! =null
  • Node[1]:array ! = ' '

Then the two parameters v1 and v2 are the left and right parameters respectively. At this point, the parameters in Node[0] are parsed first

  • v1: parametersarrayValue of the corresponding array
  • v2: is null

At this point you should be able to see why strings can be compared to empty strings, because he’s converting an array to an Object and comparing it with his equal method. And then he goes into the equal method that he wrote and he finds out that array comparisons are special.

  • If the left is an array and the right is a string: both are converted toObjectthenv1.getClass()==v2.getClass()judge
  • If the left is an array and the right is an array: Check whether the two arrays are the same length. If they are, loop over the two arrays and compare the values