MyBatis能否与Freemarker兼容?配置后标签失效问题咨询
这绝对不是Bug,而是MyBatis脚本语言机制的特性——当你把默认脚本语言设置为FreeMarker后,MyBatis会用FreeMarker的解析器来处理所有SQL语句,原来那些熟悉的MyBatis XML动态标签(<if>、<choose>这类)会被FreeMarker当成普通文本直接输出,根本不会被解析执行,这就是为什么你的SQL里会同时出现TOP和LIMIT的原因。
之前没配置FreeMarker时,MyBatis用的是默认的XML脚本语言解析器,能正确识别并执行<if>、<choose>的逻辑,所以功能正常。
下面给你几个可行的解决方案:
方案1:彻底切换到FreeMarker语法重写动态SQL
既然已经把默认脚本语言设为FreeMarker,不如直接用FreeMarker的指令来替代原来的MyBatis标签。针对你的例子,改写后的SQL应该是这样的:
<select id="find" parameterType="map" resultType="com.example.MyType"> SELECT <#if _databaseId == 'sqlserver'> TOP (${rowLimit}) </#if> FROM myexampletable <#choose> <#when _databaseId == 'oracle'> WHERE rownum <= ${rowLimit} </#when> <#when _databaseId == 'postgresql' || _databaseId == 'h2'> LIMIT ${rowLimit} </#when> </#choose> </select>
这里要注意,FreeMarker里直接用${rowLimit}引用参数就行,不需要原来的${r"#{rowLimit}"}写法,MyBatis的FreeMarker驱动会自动帮你处理参数绑定,不用担心SQL注入问题。
方案2:按需使用FreeMarker,保留默认XML脚本
如果你不想全部切换,只想在特定语句里用FreeMarker,可以去掉全局的defaultScriptingLanguage设置,只保留typeAlias,然后在需要用FreeMarker的SQL标签上通过lang属性指定:
<!-- 全局配置调整 --> <settings> <!-- 移除defaultScriptingLanguage的设置 --> </settings> <typeAliases> <typeAlias type="org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver" alias="FreeMarker"/> </typeAliases> <!-- 需要用FreeMarker的语句加上lang属性 --> <select id="findWithFreemarker" parameterType="map" resultType="com.example.MyType" lang="FreeMarker"> <!-- 这里写FreeMarker语法的SQL --> </select> <!-- 其他语句继续用原来正常的MyBatis XML动态SQL --> <select id="find" parameterType="map" resultType="com.example.MyType"> SELECT <if test="_databaseId == 'sqlserver'"> TOP (${r"#{rowLimit}"}) </if> FROM myexampletable <choose> <when test="_databaseId == 'oracle'"> WHERE rownum <= ${r"#{rowLimit}"} </when> <when test="_databaseId == 'postgresql' || _databaseId == 'h2'"> LIMIT ${r"#{rowLimit}"} </when> </choose> </select>
关于“同时混用两种语言”的说明
很遗憾,MyBatis的每个SQL语句只能由一种脚本语言解析器处理,没办法在同一个<select>标签里同时让MyBatis XML标签和FreeMarker指令都生效——解析器是排他的,要么用XML脚本解析,要么用FreeMarker解析,二者不可兼得。所以你只能二选一,或者把不同逻辑拆分到不同的SQL语句中。
内容的提问来源于stack exchange,提问作者Petr Sládek

