6、MyBatis的动态SQL

来源:互联网 发布:手机成绩统计软件 编辑:程序博客网 时间:2024/06/10 14:44

      MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

      通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

      动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

      mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:

  1. if 语句 (简单的条件判断)
  2. where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
  3. set (主要用于更新时)
  4. trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
  5. choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
  6. foreach (在实现 mybatis in 语句查询时特别有用)

if标签

目标:通过传入User对象查询符合条件的user

<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">    select * from user    where     id=#{id}    and username like "%"#{username}"%"    and sex=#{sex}    and address=#{address}</select>

这是未使用if标签前。设想一下,如果传入的User对象并没有对应的值,那么这条SQL语句就什么也查不出。
这时就需要用if标签对各个属性值进行判断。

<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">    select * from user where 1=1    <if test="id>=1">        id=#{id}    </if>    <if test="username!=null and username!=''">        and username like "%"#{username}"%"    </if>    <if test="sex!=null and sex!=''">        and sex=#{sex}    </if>    <if test="address!=null and address!=''">        and address=#{address}    </if></select>

where后面的1=1一定不能丢失。
这是为了防止两种SQL语句:

①:select * from user where②:select * from user where and username like '张'

一个是where后无条件,一个是紧跟一个and。

测试:

public void MapperTest(){    SqlSession session = sqlSessionFactory.openSession();    UserMapper mapper = session.getMapper(UserMapper.class);    User user = new User();    user.setSex("m");    user.setUsername("张");    List<User> list = mapper.queryByUser(user);    for (User u : list) {        System.out.println(u);    }    session.commit();    session.close();}

这里写图片描述


where标签

在上述if标签使用后有不满意的地方,即where后一定要有一个条件(譬如:1=1)。不让会产生SQL语句的错误。

      这时可以使用 where 动态语句来解决。“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或OR 开头的,则它会剔除掉。

使用where标签:

<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">    select * from user     <where>        <if test="id>=1">            id=#{id}        </if>        <if test="username!=null and username!=''">            and username like "%"#{username}"%"        </if>        <if test="sex!=null and sex!=''">            and sex=#{sex}        </if>        <if test="address!=null and address!=''">            and address=#{address}        </if>    </where></select>

      使用了where标签后在SQL语句中不需要写where了,会自动生成。
     我们也不需要担心出现类似select * from user where and username like ‘张’ 这样的SQL语句了,MyBatis会自动将第一个and(或or)剔除。自动变为:select * from user where username like ‘张’


set标签

未使用set标签前:

<update id="updateByUser" parameterType="cn.xpu.hcp.bean.User">    update user    set     <if test="birthday!=null">        birthday = #{birthday},    </if>    <if test="username!=null and username!=''">            username = #{username},    </if>    <if test="sex!=null and sex!=''">        sex=#{sex},    </if>    <if test="address!=null and address!=''">        address=#{address},    </if>    where id=#{id}</update>

一大问题,“,”的取舍问题,最后一个的“,”怎么办?

这也是使用set标签的好处了,set标签可以帮我们处理最后一个逗号:

<update id="updateByUser" parameterType="cn.xpu.hcp.bean.User">    update user    <set>     <if test="birthday!=null">        birthday = #{birthday},    </if>    <if test="username!=null and username!=''">            username = #{username},    </if>    <if test="sex!=null and sex!=''">        sex=#{sex},    </if>    <if test="address!=null and address!=''">        address=#{address},    </if>    </set>    where id=#{id}</update>

同样,使用set标签后sql语句中不要写set了。
测试:

public void MapperTest(){    SqlSession session = sqlSessionFactory.openSession();    UserMapper mapper = session.getMapper(UserMapper.class);    User user = new User();    user.setId(2);    user.setSex("f");    user.setUsername("孙尚香");    mapper.updateByUser(user);    session.commit();    session.close();}

这里写图片描述


trim标签

      trim 标签的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides;正因为 trim 有这样的功能,所以我们也可以非常简单的利用 trim 来代替 where /set标签的功能。

代替where标签:

select * from user <trim prefix="where" suffixOverrides="and"><!--如果是要去除or就写or-->    <if test="id>=1">        id=#{id}    </if>    <if test="username!=null and username!=''">        and username LIKE "%"#{username}"%"    </if>    <if test="sex!=null and sex!=''">        and sex=#{sex}    </if>    <if test="address!=null and address!=''">        and address=#{address}    </if></trim>

代替set标签:

update user<trim prefix="set" suffixOverrides=",">    <if test="birthday!=null">        birthday = #{birthday},    </if>    <if test="username!=null and username!=''">            username = #{username},    </if>    <if test="sex!=null and sex!=''">        sex=#{sex},    </if>    <if test="address!=null and address!=''">        address=#{address},    </if>    where id=#{id}</trim>

choose标签

      choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。

 <select id="testChoose" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">    select * from user    <where>        <choose>            <when test="id>=1">                id=#{id}            </when>            <when test="username!=null and username!=''">                and username LIKE "%"#{username}"%"            </when>            <when test="sex!=null and sex!=''">                and sex=#{sex}            </when>            <when test="address!=null and address!=''">                and address=#{address}            </when>            <otherwise>            </otherwise>        </choose>    </where></select>

测试:

public void MapperTest(){        SqlSession session = sqlSessionFactory.openSession();        UserMapper mapper = session.getMapper(UserMapper.class);        User user = new User();        user.setSex("f");        user.setUsername("张");        List<User> list = mapper.testChoose(user);        for (User u : list) {            System.out.println(u);        }        session.commit();        session.close();    }

这里写图片描述
如果使用if标签则会查出有”张”字并且性别为“f”的人,使用了choose标签后,由于已经满足了有“张”字这一条件,就不会进行下面的性别判断了。


foreach标签

向SQL语句传递数组或集合,MyBatis使用foreach进行解析。
比如:
select * from user where id in (1,5,6)

解析包装类中的集合/数组

在包装类中声明集合ids,并且添加getter/setter方法:

public class UserWrapper {    private List<Integer> ids;    public List<Integer> getIds() {        return ids;    }    public void setIds(List<Integer> ids) {        this.ids = ids;    }}

在UserMapper.xml中:

<select id="testForeach" parameterType="cn.xpu.hcp.bean.UserWrapper" resultType="cn.xpu.hcp.bean.User">        select * from user        <!--             collection:遍历的集合            item:遍历的项目,名字任意,只需要与下面#{}中的相同即可            open:在前面添加的sql片段            close:在结尾添加的sql片段            separator:以什么为分割符,这里选择","            如此组成sql语句:select * from user where id in (#{item},...)         -->        <where>            <foreach collection="ids" item="item" open="id in(" close=")" separator=",">                #{item}            </foreach>        </where></select>

测试:

public void MapperTest(){    SqlSession session = sqlSessionFactory.openSession();    UserMapper mapper = session.getMapper(UserMapper.class);    UserWrapper wrapper = new UserWrapper();    List<Integer> ids = new ArrayList<>();    ids.add(2);    ids.add(3);    ids.add(10);    ids.add(13);    wrapper.setIds(ids);    List<User> list = mapper.testForeach(wrapper);    for (User u : list) {        System.out.println(u);    }    session.commit();    session.close();}

这里写图片描述

解析直接传递的数组/集合

测试类:

public void MapperTest(){    SqlSession session = sqlSessionFactory.openSession();    UserMapper mapper = session.getMapper(UserMapper.class);    Integer[] ids = {1,3,10,15};    List<User> list = mapper.testForeach(ids);//直接传递数组    for (User u : list) {        System.out.println(u);    }    session.commit();    session.close();}

此时的foreach中:

<foreach collection="array" item="item" open="id in(" close=")" separator=",">    ......</foreach>

注意collection的变化,此时的值不应该为ids,而是array,所以如果传递集合就为list。

原创粉丝点击