不学无数——Mybatis解析判断表达式源码分析
source link: https://studygolang.com/articles/15449?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Mybatis解析判断表达式源码分析
在我们开发过程中用 Mybatis
经常会用到下面的例子
Mapper如下
Map<String ,String > testArray(@Param("array") String [] array);
XMl中的sql如下
<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>
刚看上面的代码会觉得数组怎么能和空字符串进行一起比较呢,一开始会觉得这个代码运行起来绝对报错,但是写单元测试运行了一遍发现成功运行了。因此想是不是 Mybatis
在内部对数组类型的数据进行了封装。于是有了这一次的源码解析之旅。上网查了查发现 Mybatis
解析使用了 OGNL
。至于什么是 OGNL
摘抄了百度百科中的一段话
OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。这样可以更好的取得数据。
单元测试类如下
@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); }
首先我们先来看一下 DynamicSqlSource
这个类,这个类中有个方法如下
@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; }
其中
rootSqlNode.apply(context);
这段代码对SQL进行了动态的拼接,然后点进去看一下
@Override public boolean apply(DynamicContext context) { for (SqlNode sqlNode : contents) { sqlNode.apply(context); } return true; }
这里的SQL拼接运用了 组合模式 不同的 sqlNode
调用的方法不一样,但是最后的想要结果都是一样的:拼接SQL。例如我们第一次进 apply
这个方法中的时候他跳转到了
StaticTextSqlNode
这个类中调用了下面的方法
@Override public boolean apply(DynamicContext context) { context.appendSql(text); return true; }
直接将SQL拼接为
select * from t_ams_ac_pmt_dtl where cpt_pro=#{cptProp}
然后我们第二次循环执行发现它跳转到了 IfSqlNode
这个类中,这是标签为 <if>
的判断类,
@Override public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; }
在解析语句中传了两个参数进去
evaluator.evaluateBoolean(test, context.getBindings())
-
test
:就是要解析的表达式,在此场景下就是array!=null and array != ''
-
context.getBindings()
:获得的是一个Map,其中存储了参数array
的所对应的值,如下所示
image
然后接下来就到了 OGNL
解析表达式了,发现最后到了 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; }
这里解析分为了两步进行解析,上面的表达式为 array!=null and array != ''
那么他会根据and 进行分组将其放入 Node
数组中。
-
Node[0]
:array!=null
-
Node[1]
:array != ''
然后这里面的两个参数 v1
和 v2
分别为左边和右边的参数,此时先解析 Node[0]
中的参数
-
v1
:就是参数array
对应的数组的值 -
v2
:就是null
此时到这应该就知道为什么 String
数组为什么能和空字符串进行比较了,因为他将数组转化为了 Object
然后用自己写的 equal
方法进行比较。然后进去他写的 equal
方法中看了以后发现他对数组比较是特殊的。
- 如果左边是数组右边是字符串:两个都转换为
Object
然后进行v1.getClass()==v2.getClass()
判断 - 如果左边是数组右边也是数组:先判断两个数组的长度是否相同,如果相同,那么循环遍历两个数组进行里面的值的比较
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK