扣丁学堂Java培训之MyBatis执行动态SQL方法详解

2019-02-14 13:18:33 413浏览

今天扣丁学堂Java培训老师给大家分享一篇关于MyBatis执行动态SQL的方法详解,希望对大家学习Java开发有所帮助,下面我们一起来看一下吧。



大家基本上都知道如何使用MyBatis执行任意SQL,使用方法很简单,例如在一个XXMapper.xml中:

<select id="executeSql" resultType="map">
  ${_parameter}
</select>

你可以如下调用:

sqlSession.selectList("executeSql", "select * from sysuser where enabled = 1");

或者你可以在XXMapper.java接口中定义如下方法:

List<Map> executeSql(String sql);

然后使用接口调用方法:

xxMapper.executeSql("select * from sysuser where enabled = 1");

上面这些内容可能都会,下面在此基础上再复杂一点。

假如像上面SQL中的enabled=1我想使用参数方式传值,也就是写成enabled=#{enabled},如果你没有遇到过类似这种需求,可能不明白为什么要这么写,举个例子,要实现一种动态查询,可以在前台通过配置SQL,提供一些查询条件就能实现一个查询的功能(为了安全,这些配置肯定是开发或者实施做的,不可能让用户直接操作数据库)。

针对这个功能,使用MyBatis实现起来相当容易。配置SQL肯定要执行,用上面讲的这种方式肯定可以执行SQL,如何提供参数呢?参数就是enabled=#{enabled}中的#{enabled}部分。如果再多一些条件,一个配置好的SQL如下:

select * from sysuser 
where enabled = #{enabled} 
and userName like concat('%',#{userName},'%')

这种情况下,该怎么用MyBatis实现呢?

首先XML中修改如下:

<select id="executeSql" resultType="map">
  ${sql}
</select>

接口中的方法修改为:

List<Map> executeSql(Map map);

然后调用方法:

Map map = new HashMap();
//这里的 sql 对应 XML 中的 ${sql}
map.put("sql", "select * from sysuser "
    + " where enabled = #{enabled} "
    + " and userName like concat('%',#{userName},'%')");
//#{enabled}
map.put("enabled", 1);
//#{userName}
map.put("userName", "admin");
//接口方式调用
List<Map> list = xxMapper.executeSql(map);
//sqlSession方式调用
sqlSession.selectList("executeSql", map);

有了这个SQL之后,就可以将enabled和userName作为条件提供给用户。这两个条件显然是必填的。如果是可选的,那该怎么写?

也许有人想到了是不是可以用MyBatis中的动态SQL,使用<if>标签等等?

再回答这个问题前,我们先看处理动态SQL的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;
}

MyBatis处理动态SQL时,所有动态SQL的标签都会处理为SqlNode(这里的rootSqlNode)对象,包含${}的也会处理为TextSqlNode对象,在上面方法的前两行,就是MyBatis处理动态SQL的地方。

因此如果我们在${sql}中的内容包含嵌套的${}和<if>,<where>等标签时,他们在MyBatis解析XML为SqlNode对象时,XML<select>元素包含的内容只有${sql},只有${sql}会被解析,在运行时这个参数字符串中可能包含的${}和<if>,<where>等标签,但是这都发生在MyBatis解析后,因此当这些内容作为字符串中的一部分出现时,他们不会被特殊处理,他们只是SQL中的一部分,只是原样输出(由于数据库不认会报错)无法被处理,因此没法通过MyBatis自带的这种方式来写动态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 * from sysuser 
where 1 = 1
<#if enabled??>
enabled = #{enabled} 
</#if>
<#if userName?? && userName != ''>
and userName like concat('%',#{userName},'%')
</#if>

注意,这里的<#if>是Freemarker的元素。在不考虑SQL注入的情况下,上面的SQL还可以写成:

select * from sysuser 
where 1 = 1
<#if enabled??>
enabled = #{enabled} 
</#if>
<#if userName?? && userName != ''>
and userName like '%${userName}%'
</#if>

区别就是'%${userName}%',因为Freemarker也会处理${userName},也会用实际的值来替换这里的参数。

在前面调用的代码中,这里修改如下:

//#{enabled}
map.put("enabled", 1);
//#{userName}
map.put("userName", "admin");
//这里的 sql 对应 XML 中的 ${sql}
String sql = "上面两个复杂SQL中的一个";
//使用Freemarker处理sql
sql = processSqlByFreemarker(sql, map);
//将处理后的sql放到map中
map.put("sql", sql);
//执行方法
List<Map> list = xxMapper.executeSql(map);

注:processSqlByFreemarker方法就是根据map中的数据来处理sql字符串,实现方式可以自己搜索。

到这里,一个不是很复杂的动态SQL功能就实现了。

不知道有没有更贪心的人,你会不会想,上面返回值都是List<Map>类型,能不能返回一个我指定的实体类呢?

例如在map中:

map.put("class", "tk.mybatis.model.SysUser");

能不能通过这种方式让返回值变成SysUser类型呢?由于这篇文章耗时已经太长,这里就提供一个方案,不深入。

你可以使用拦截器实现,获取MappedStatement后,复制一份,然后修改resultMaps中resultMap的type属性为你指定的class类型就能实现,说起来容易,实际操作起来能有PageHelper分页插件1/10左右的工作量。

注:如果是动态的update,insert,delete语句,可以将上面的<select>改为<update>(不需要使用<delete>和<insert>),返回值用int,比select的情况容易很多。

以上就是扣丁学堂Java培训之MyBatis执行动态SQL方法详解的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,最后想要了解更多请关注扣丁学堂Java培训官网、微信等平台,扣丁学堂IT职业在线学习教育平台不仅为您提供权威的Java视频教程供大家学习,还精心的准备了Java从入门到精通开发实战技能,定能让你学有所成。扣丁学堂Java技术交流群:670348138


扣丁学堂微信公众号


【关注微信公众号获取更多学习资料】


查看更多关于“Java开发资讯”的相关文章>>

标签: Java培训 Java视频教程 Java多线程 Java面试题 Java学习视频 Java开发

热门专区

暂无热门资讯

课程推荐

微信
微博
15311698296

全国免费咨询热线

邮箱:codingke@1000phone.com

官方群:148715490

北京千锋互联科技有限公司版权所有   北京市海淀区宝盛北里西区28号中关村智诚科创大厦4层
京ICP备12003911号-6   Copyright © 2013 - 2019

京公网安备 11010802030908号