MyBatis执行动态SQL的方法
大家基本上都知道如何使用MyBatis执行任意SQL,使用方法很简单,例如在一个XXMapper.xml中:
${_parameter}
你可以如下调用:
sqlSession.selectList("executeSql","select*fromsysuserwhereenabled=1");
或者你可以在XXMapper.java接口中定义如下方法:
List
然后使用接口调用方法:
xxMapper.executeSql("select*fromsysuserwhereenabled=1");
上面这些内容可能都会,下面在此基础上再复杂一点。
假如像上面SQL中的enabled=1我想使用参数方式传值,也就是写成enabled=#{enabled},如果你没有遇到过类似这种需求,可能不明白为什么要这么写,举个例子,要实现一种动态查询,可以在前台通过配置SQL,提供一些查询条件就能实现一个查询的功能(为了安全,这些配置肯定是开发或者实施做的,不可能让用户直接操作数据库)。
针对这个功能,使用MyBatis实现起来相当容易。配置SQL肯定要执行,用上面讲的这种方式肯定可以执行SQL,如何提供参数呢?参数就是enabled=#{enabled}中的#{enabled}部分。如果再多一些条件,一个配置好的SQL如下:
select*fromsysuser whereenabled=#{enabled} anduserNamelikeconcat('%',#{userName},'%')
这种情况下,该怎么用MyBatis实现呢?
首先XML中修改如下:
${sql}
接口中的方法修改为:
List
然后调用方法:
Mapmap=newHashMap(); //这里的sql对应XML中的${sql} map.put("sql","select*fromsysuser" +"whereenabled=#{enabled}" +"anduserNamelikeconcat('%',#{userName},'%')"); //#{enabled} map.put("enabled",1); //#{userName} map.put("userName","admin"); //接口方式调用 List
有了这个SQL之后,就可以将enabled和userName作为条件提供给用户。这两个条件显然是必填的。如果是可选的,那该怎么写?
也许有人想到了是不是可以用MyBatis中的动态SQL,使用
再回答这个问题前,我们先看处理动态SQL的DynamicSqlSource中的代码:
@Override publicBoundSqlgetBoundSql(ObjectparameterObject){ DynamicContextcontext=newDynamicContext(configuration,parameterObject); rootSqlNode.apply(context); SqlSourceBuildersqlSourceParser=newSqlSourceBuilder(configuration); Class>parameterType= parameterObject==null?Object.class:parameterObject.getClass(); SqlSourcesqlSource=sqlSourceParser.parse(context.getSql(), parameterType,context.getBindings()); BoundSqlboundSql=sqlSource.getBoundSql(parameterObject); for(Map.Entryentry:context.getBindings().entrySet()){ boundSql.setAdditionalParameter(entry.getKey(),entry.getValue()); } returnboundSql; }
MyBatis处理动态SQL时,所有动态SQL的标签都会处理为SqlNode(这里的rootSqlNode)对象,包含${}的也会处理为TextSqlNode对象,在上面方法的前两行,就是MyBatis处理动态SQL的地方。
因此如果我们在${sql}中的内容包含嵌套的${}和
提示
在上面的代码中:
sqlSourceParser.parse(context.getSql(),parameterType,context.getBindings());
这一段代码是处理动态参数(#{})的
这个处理在动态SQL处理之后,
因此可以在SQL中使用这种类型的参数。
既然不能用MyBatis动态SQL方式,该怎么实现动态SQL呢?
这里提供一个简单的思路,在SQL中使用模板标记语言来实现动态SQL(例如freemarker),在SQL交给MyBatis执行之前,使用模板对SQL进行处理生成最终执行的SQL(需要避免处理#{}参数),将这个SQL交给MyBatis执行。
举一个Freemarker模板的例子,仍然以上面的SQL为基础:
select*fromsysuser where1=1 <#ifenabled??> enabled=#{enabled} #if> <#ifuserName??&&userName!=''> anduserNamelikeconcat('%',#{userName},'%') #if>
注意,这里的<#if>是Freemarker的元素。在不考虑SQL注入的情况下,上面的SQL还可以写成:
select*fromsysuser where1=1 <#ifenabled??> enabled=#{enabled} #if> <#ifuserName??&&userName!=''> anduserNamelike'%${userName}%' #if>
区别就是'%${userName}%',因为Freemarker也会处理${userName},也会用实际的值来替换这里的参数。
在前面调用的代码中,这里修改如下:
//#{enabled} map.put("enabled",1); //#{userName} map.put("userName","admin"); //这里的sql对应XML中的${sql} Stringsql="上面两个复杂SQL中的一个"; //使用Freemarker处理sql sql=processSqlByFreemarker(sql,map); //将处理后的sql放到map中 map.put("sql",sql); //执行方法 List
注:processSqlByFreemarker方法就是根据map中的数据来处理sql字符串,实现方式可以自己搜索。
到这里,一个不是很复杂的动态SQL功能就实现了。
不知道有没有更贪心的人,你会不会想,上面返回值都是List
例如在map中:
map.put("class","tk.mybatis.model.SysUser");
能不能通过这种方式让返回值变成SysUser类型呢?由于这篇文章耗时已经太长,这里就提供一个方案,不深入。
你可以使用拦截器实现,获取MappedStatement后,复制一份,然后修改resultMaps中resultMap的type属性为你指定的class类型就能实现,说起来容易,实际操作起来能有PageHelper分页插件1/10左右的工作量。
注:如果是动态的update,insert,delete语句,可以将上面的
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。如果你想了解更多相关内容请查看下面相关链接