栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MyBatis学习 - 动态 sql

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MyBatis学习 - 动态 sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

在开始之前先创建数据库:

DROp DATAbase IF EXISTS `mybatis_db`;
CREATE DATAbase `mybatis_db`;
USE `mybatis_db`;
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '用户id',
  name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '用户名',
  age SMALLINT NOT NULL DEFAULT 1 COMMENT '年龄'
) COMMENT '用户表';
INSERT INTO t_user VALUES (1,'路人甲Java',30),(2,'张学友',50),(3,'刘德华',50);
1、if

相当于java中的if判断,语法:


需要追加的sql

test的值为一个判断表达式,写法上采用OGNL表达式的方式。

当test成立的时候,if体内部的sql会被拼接上。

如:


    SELECT id,name,age FROM t_user
    
        
            AND id = #{id}
        
        
            AND name = #{name}
        
        
            AND age = #{age}
        
    

也可以实现前面的效果。

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

现在我们想通过用户id更新用户信息,参数为User对象,对象中的属性如果不为空,就进行更新,我们可以这么写:


    UPDATE t_user SET
    
        name = #{name},
    
    
        age = #{age},
    
    
        
            AND id = #{id}
        
    

我们来看一下,当所有属性都传值了,sql变成了下面这样:

UPDATE t_user SET name = ?, age = ?, where id = ?

上面这个sql是有问题的,where前面多了一个逗号,得想办法将这个逗号去掉,这个逗号属于最后一个需要更新的字段后面的逗号,属于多余的,mybatis中提供了set元素来解决这个问题,将上面的代码改成下面这样:


    UPDATE t_user
    
        
            name = #{name},
        
        
            age = #{age},
        
    
    
        
            AND id = #{id}
        
    

我们将sql中的set去掉了,加了个set元素,set元素会对其内部拼接的sql进行处理,会将这部分sql前后的逗号给去掉并在前面加上set。

当传入id和age的时候,生成的sql:

UPDATE t_user SET age = ? where id = ?

同样的 set 元素也可以用 trim 替代:


  ...

那么前面的例子可以写成:

```xml

    UPDATE t_user
    
        
            name = #{name},
        
        
            age = #{age},
        
    
    
        
            AND id = #{id}
        
    

  • 对 trim 元素总结一下:


trim元素内部可以包含各种动态sql,如where、chose、sql等各种元素,使用trim包含的元素,mybatis处理过程:

  1. 先对trim内部的sql进行拼接,比如这部分sql叫做sql1
  2. 将sql1字符串前面的部分中包含trim的prefixOverrides指定的部分给去掉,得到sql2
  3. 将sql2字符串后面的部分中包含trim的suffixOverrides指定的部分给去掉,得到sql3
  4. 在sql3前面追加trim中prefix指定的值,得到sql4
  5. 在sql4后面追加trim中suffix指定的值,得到最终需要拼接的sql5

再看一个例子:


    UPDATE t_user
    
        
            name = #{name},
        
        
            age = #{age},
        
    
    
        
            AND id = #{id}
        
    

上面的prefixOverrides和suffixOverrides都设置的是逗号,表示trim内部的sql前后的逗号会被去掉,最后会在前面拼接一个prefix指定的set。

大家有兴趣的可以去看一下trim的java实现,代码下面这个类中:

org.apache.ibatis.scripting.xmltags.TrimSqlNode

实际上where和set的实现是继承了TrimSqlNode,where对应的java代码:

public class WhereSqlNode extends TrimSqlNode {
  private static List prefixList = Arrays.asList("AND ","OR ","ANDn", "ORn", "ANDr", "ORr", "ANDt", "ORt");
  
  public WhereSqlNode(Configuration configuration, SqlNode contents) {
    super(configuration, contents, "WHERe", prefixList, null, null);
  }
  
}

set对应的 java代码:

public class SetSqlNode extends TrimSqlNode {
  private static final List COMMA = Collections.singletonList(",");
  
  public SetSqlNode(Configuration configuration,SqlNode contents) {
    super(configuration, contents, "SET", COMMA, null, COMMA);
  }
  
}

最后都是依靠TrimSqlNode来实现的。

4、foreach

相当于java中的循环,可以用来遍历数组、集合、map等。


动态sql部分

  • collection:可以是一个List、Set、Map或者数组
  • item:集合中的当前元素的引用
  • index:用来访问当前元素在集合中的位置
  • separator:各个元素之间的分隔符
  • open和close用来配置最后用什么前缀和后缀将foreach内部所有拼接的sql给包装起来。

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。

我们对案例1做个改造,map中支持放入用户的id列表(ArrayList),对应的key为idList,然后支持多个用户id查询,此时我们需要用in来查询,实现如下: