MyBatis使用大全(4)------动态SQL使用

来源:互联网 发布:武汉软件公司 知乎 编辑:程序博客网 时间:2024/05/18 00:26

MyBatis 的强大特性之一便是它的动态 SQL。

如果你用jdbc等等每次拼接sql都需要特别注意空格,未列的逗号。

但是,利用MyBatis动态SQL对于这些问题会有一些改善,用起来会

相对省心。通常使用动态 SQL 不可能是独立的一部分,

MyBatis当然使用一种强大的动态 SQ 语言来改进这种情形,

这种语言可以被用在任意的 SQL 映射语句中。

动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。

在 MyBatis 之前的版本中,有很多的元素需要来了解。

MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以。

MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

常用元素如下:

--if

--choose (when, otherwise)

--trim (where, set)

--foreach

接下来对这些元素进行讨论:

(1) if的使用

动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:

Java代码查询方法封装参数map:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. Map<String,Object> parMap = new HashMap<String, Object>();  
  2. parMap.put("id"1);  
  3. UserMainMapper userMainMapper = session.getMapper(UserMainMapper.class);  
  4. User user = userMainMapper.queryUserMainById(parMap);  
  5. System.out.println(user.getUsername());  
对应的映射文件查询方法:
[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <!DOCTYPE mapper    
  3.   PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"    
  4.   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">    
  5.     
  6. <!--   
  7.     为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+去掉sql映射文件后缀的文件名,  
  8.     这样就能够保证namespace的值是唯一的,例如namespace="com.lanhuigu.mybatis.map.UserMainMapper"  
  9.     就是com.lanhuigu.mybatis.map(包名)+UserMainMapper(UserMainMapper.xml文件去除后缀)  
  10. -->  
  11. <mapper namespace="com.lanhuigu.mybatis.map.UserMainMapper">    
  12.   <!--  
  13.      在select标签中编写查询的SQL语句, 设置select标签的id属性为queryUserMainById,id属性值必须是唯一的,  
  14.      不能够重复使用parameterType属性指明查询时使用的参数类型,resultType属性指明查询返回的结果集类型  
  15.      resultType="com.lanhuigu.mybatis.entity.User"就表示将查询结果封装成一个User类的对象返回  
  16.      User类就是users表所对应的实体类  
  17.   -->  
  18.   <select id="queryUserMainById" parameterType="java.util.Map"   
  19.           resultType="com.lanhuigu.mybatis.entity.User">    
  20.      select     
  21.         f_id id,    
  22.         f_username username,    
  23.         f_age age    
  24.      from t_user_main     
  25.      where 1=1   
  26.      <if test="id != null">  
  27.         and f_id = #{id}  
  28.      </if>  
  29.   </select>    
  30. </mapper>    
在该查询方法中,parameterType="java.util.Map"指定传入参数的类型为map,

在where条件后,我们加了一个对id是否为空的判断,如果不为空,则作为查询条件之一,

否则,不做为查询条件。

当然了,我们能判断一个条件,我们也能判断多个条件,比如map多封装几个有用的参数,

查询条件同样可以进行拆分或组合判断。

比如map封装:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. Map<String,Object> parMap = new HashMap<String, Object>();  
  2. parMap.put("id"1);  
  3. parMap.put("username""testMyBatis");  
查询条件if判断部分做修改:
[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <select id="queryUserMainById" parameterType="java.util.Map"   
  2.           resultType="com.lanhuigu.mybatis.entity.User">    
  3.    select     
  4.       f_id id,    
  5.       f_username username,    
  6.       f_age age    
  7.    from t_user_main     
  8.    where 1=1   
  9.    <if test="id != null and username!=null">  
  10.     and f_id = #{id}  
  11.     and f_username = #{username}  
  12.    </if>  
  13. </select>    
我们可以看到,在以前id判断基础条件上加了一个username的判断。

注意:对于if的应用我们可以根据实际情况判断,可以单个判断,可以多个条件判断。

2.choose, when, otherwise的使用

有些时候,我们不想用到所有的条件语句,而只想从中择其一二。

针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

这种处理方式在Oracle,MySQL数据库中条件判断逻辑一样。咱们讲上面例子改改,例如:

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <!DOCTYPE mapper    
  3.   PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"    
  4.   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">    
  5.     
  6. <!--   
  7.     为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+去掉sql映射文件后缀的文件名,  
  8.     这样就能够保证namespace的值是唯一的,例如namespace="com.lanhuigu.mybatis.map.UserMainMapper"  
  9.     就是com.lanhuigu.mybatis.map(包名)+UserMainMapper(UserMainMapper.xml文件去除后缀)  
  10. -->  
  11. <mapper namespace="com.lanhuigu.mybatis.map.UserMainMapper">    
  12.   <!--  
  13.      在select标签中编写查询的SQL语句, 设置select标签的id属性为queryUserMainById,id属性值必须是唯一的,  
  14.      不能够重复使用parameterType属性指明查询时使用的参数类型,resultType属性指明查询返回的结果集类型  
  15.      resultType="com.lanhuigu.mybatis.entity.User"就表示将查询结果封装成一个User类的对象返回  
  16.      User类就是users表所对应的实体类  
  17.   -->  
  18.   <select id="queryUserMainById" parameterType="java.util.Map"   
  19.           resultType="com.lanhuigu.mybatis.entity.User">    
  20.      select     
  21.         f_id id,    
  22.         f_username username,    
  23.         f_age age    
  24.      from t_user_main     
  25.      where 1=1   
  26.      <choose>  
  27.         <when test="id != null">  
  28.             and f_id = #{id}  
  29.         </when>  
  30.         <when test="username != null">  
  31.             and f_username = #{username}  
  32.         </when>  
  33.         <otherwise>  
  34.             and f_id = 10  
  35.         </otherwise>  
  36.      </choose>  
  37.   </select>    
  38. </mapper>    
逻辑判断部分很明显,当id不为空,加上条件,当username不为空加上条件,

但是如果都为空,强制查询f_id=10的数据。

3.trim, where, set的使用

我在上面的例子中where后面都加1=1,加这个的原因主要是防止生成的sql是这样的,

select   
        f_id id,  
        f_username username,  
        f_age age  
     from t_user_main   
     where  and f_id = #{id}

这条sql执行的时候必然报错,我在前面加个1=1就可以轻松避免。让sql避免异常:

select   
        f_id id,  
        f_username username,  
        f_age age  
     from t_user_main   
     where 1=1 and f_id = #{id}

处理这样的问题还有另外一个方法,使用where元素:

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <select id="queryUserMainById" parameterType="java.util.Map"   
  2.           resultType="com.lanhuigu.mybatis.entity.User">    
  3.    select     
  4.       f_id id,    
  5.       f_username username,    
  6.       f_age age    
  7.    from t_user_main     
  8.    <where>  
  9.     <choose>  
  10.         <when test="id != null">  
  11.             f_id = #{id}  
  12.         </when>  
  13.         <when test="username != null">  
  14.             and f_username = #{username}  
  15.         </when>  
  16.         <otherwise>  
  17.             and f_id = 10  
  18.         </otherwise>  
  19.       </choose>  
  20.    </where>  
  21. </select>  
使用where元素后如上写法,肉眼上看去,当我们的id为空时,我们是不是会生成这样的sql:
select   
        f_id id,  
        f_username username,  
        f_age age  
     from t_user_main where and f_username = #{username}

这个sql的where后直接跟了个and执行的时候为什么不报错,这都是where元素的功劳。

where知道,有大于等于1个条件时,才会去注入"where"字句,若最后的内容是“AND”或“OR”开头的,

where 元素也知道如何将他们去除。这就是我们为什么肉眼看上去and....,这个语句却能执行的原因。

如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。

比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE"prefixOverrides="AND |OR "> ...</trim>

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。

它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。

类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <update id="updateUserMain" parameterType="java.util.Map">  
  2.     update t_user_main   
  3.         <set>  
  4.             <if test="username != null">f_username = #{username}</if>  
  5.             <if test="age != null">f_age = #{age}</if>  
  6.         </set>  
  7.       where f_id = #{id}  
  8. bsp;</update>   
这里,set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,

因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。

4.foreach的使用

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <select id="queryUserMainById" resultType="com.lanhuigu.mybatis.entity.User">    
  2.    select     
  3.       f_id id,    
  4.       f_username username,    
  5.       f_age age    
  6.    from t_user_main     
  7.    where f_id in   
  8.    <foreach collection="item="item" index="index" collection="list" open="(" separator="," close=")">  
  9.       #{item}  
  10.    </foreach>  
  11. </select>  
foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。

它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。

这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。

当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。

当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。

5.bind的使用

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:

[html] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <select id="queryUserMainById" resultType="com.lanhuigu.mybatis.entity.User">  
  2.     <bind name="pattern" value="'%' + _parameter.getUsername() + '%'" />  
  3.  select     
  4.       f_id id,    
  5.       f_username username,    
  6.       f_age age    
  7.    from t_user_main  
  8. where f_username like #{pattern}  
  9. </select>  
其实这个绑定一般不用,因为我们有sql元素,用起来更灵活,更方便。
0 0
原创粉丝点击