问题记录:
使用ibatis开发的时候使用dynamic标签做动态sql拼接的时候
<dynamic prepend="where"> t.ATTRIBUTE10 in ('attribute10') <isNotEmpty prepend="AND" property="attribute1"> t.ATTRIBUTE1 = #attribute1# </isNotEmpty> </dynamic>
拼接出来的sql 为 where t.ATTRIBUTE10 in ('attribute10') t.ATTRIBUTE1 = #attribute1# 当中缺了and关键字,导致查询sql的时候报
java.sql.SQLException: sql injection violation, syntax error: TODO :
的错误。
解决问题:
为什么没有加上and关键词?
猜测一:
在sqlsessionTemplate初始化的时候就没标记上
在容器初始化MappedStatementConfig打上断点开始排查
发现dynamic标签的都有成功的标记为sqlText和sqlTag类型,并没有漏掉and关键字
都有标记成功,说明在初始化的时候并没有漏掉and关键字
从中还可以得出一个结论,在容器初始化的时候,ibatis就会开始解析xml里的sql,并封装成一个个children类型的对象数据,将里面的标签标记为sqlText对象或者sqlTag对象
猜测二:
在拼接的时候由于某些判断没拼接上
重新断点,将断点打在查询的逻辑上
最终将方法定位在DynamicSql对象中 processBodyChildren()方法中的 doPrepend() 方法上,在这个方法上
断点打入,首先sql容器会从谷歌初始化完的children拿到所有这个查询下的数据,然后开始做解析,首先解析第一个sqlText类型,普通的select * from xxx查询的String append到out对象上,然后解析第二个SqlTag类型,这个就是dynamic标签中的所有都存储在这个对象中,打开来看
children里存着sqlText,sqlTag,sqlText类型,分别对应着
t.ATTRIBUTE10 in ('attribute10')
<isNotEmpty prepend="AND" property="attribute1"> t.ATTRIBUTE1 = #attribute1# </isNotEmpty>
和一串空字符
继续往下执行,解析到isnotEmpty这个标签的sqlTag的时候,继续递归进去,里面也是sqlText
将这个sqlText拿出来,返回之后
body里面就是 isnotEmpty 标签里的 t.ATTRIBUTE1 = #attribute1#,tag里就是前序的t.ATTRIBUTE10 in ('attribute10') 这个String,然后就会进到doPrepend() 这个方法中去拼接 and
走到这步发现,他并没有拼接 insert and 而是去执行了 disableRemoveFirstPrependMarker()将第一次栈中的设置为了false。也就是说,
ibatis并没有对前序的sqlText = t.ATTRIBUTE10 in ('attribute10') 做判断,而是默认这个t.ATTRIBUTE1 = #attribute1# 为第一个,所以不需要 + and 关键字,所以就没有拼接上and,但后续isnotempty由于不是第一个,自然就会走到else的逻辑判断中,拼接上and关键字。
所以修改写法,将 isnotempty 标签的动态sql放到第一个,这样sql就能正常拼写成功,并查询成功了。
注:ibatis使用版本:2.3.4