7.4 动态 SQL

 

7.4 动态 SQL

动态 SQL 是 MyBatis 中非常强大的一个功能。例如一些常见的查询场景:

  • 查询条件不确定
  • 批量插入
  • ….

这些类似需求,我们都可以通过 MyBatis 提供的动态 SQL 来解决。

MyBatis 中提供的动态 SQL 节点非常多。

7.4.1 if

if 是一个判断节点,如果满足某个条件,节点中的 SQL 就会生效。例如分页查询,要传递两个参数,页码和查询的记录数,如果这两个参数都为 null,那我就查询所有。

我们首先来定义接口方法:

List<User> getUserByPage(@Param("start") Integer start, @Param("count") Integer count);

接口定义成功后,接下来在 XML 中定义 SQL:

<select id="getUserByPage" resultType="org.javaboy.mybatis.model.User">
    select * from user
    <if test="start !=null and count!=null">
        limit #{start},#{count}
    </if>
</select>

if 节点中,test 表示判断条件,如果判断结果为 true,则 if 节点的中的 SQL 会生效,否则不会生效。也就是说,在方法调用时,如果分页的两个参数都为 null,则表示查询所有数据:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.getUserByPage(null, null);
        System.out.println(list);
        list = mapper.getUserByPage(2, 2);
        System.out.println(list);
        sqlSession.commit();
    }
}

7.4.2 where

where 用来处理查询参数。例如我存在下面一个查询函数:

List<User> getUserByUsernameAndId(@Param("id") Integer id, @Param("name") String name);

这个查询的复杂之处在于:每个参数都是可选的,如果 id 为 null,则表示根据 name 查询,name 为 null,则表示根据 id 查询,两个都为 null,表示查询所有。

<select id="getUserByUsernameAndId" resultType="org.javaboy.mybatis.model.User">
    select * from user
    <where>
        <if test="id!=null">
            and id>#{id}
        </if>
        <if test="name!=null">
            and username like concat('%',#{name},'%')
        </if>
    </where>
</select>

用 where 节点将所有的查询条件包起来,如果有满足的条件,where 节点会自动加上,如果没有,where 节点也将不存在,在有满足条件的情况下,where 还会自动处理 and 关键字。

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.getUserByUsernameAndId(2, "java");
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(null, "javaboy");
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(5, null);
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(null, null);
        System.out.println(list);
    }
}

7.4.3 foreach

foreach 用来处理数组/集合参数。

例如,我们有一个批量查询的需求:

List<User> getUserByIds(@Param("ids")Integer[] ids);

对应的 XML 如下:

<select id="getUserByIds" resultType="org.javaboy.mybatis.model.User">
    select * from user where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>

在 mapper 中,通过 foreach 节点来遍历数组,collection 表示数组变量,open 表示循环结束后,左边的符号,close 表示循环结束后,右边的符号,item 表示循环时候的单个变量,separator 表示循环的元素之间的分隔符。

注意,默认情况下,无论你的数组/集合参数名字是什么,在 XML 中访问的时候,都是 array,开发者可以通过 @Param 注解给参数重新指定名字。

例如我还有一个批量插入的需求:

Integer batchInsertUser(@Param("users") List<User> users);

然后,定义该方法对应的 mapper:

<insert id="batchInsertUser">
    insert into user (username,address) values 
    <foreach collection="users" separator="," item="user">
        (#{user.username},#{user.address})
    </foreach>
</insert>

然后,在 Main 方法中进行测试:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = new ArrayList<>();
        User u1 = new User();
        u1.setUsername("zhangsan");
        u1.setAddress("shenzhen");
        users.add(u1);
        User u2 = new User();
        u2.setUsername("lisi");
        u2.setAddress("广州");
        users.add(u2);
        mapper.batchInsertUser(users);
        sqlSession.commit();
    }
}

7.4.4 sql 片段

大家知道,在 SQL 查询中,一般不建议写 *,因为 select * 会降低查询效率。但是,每次查询都要把字段名列出来,太麻烦。这种使用,我们可以利用 SQL 片段来解决这个问题。

例如,我们先在 mapper 中定义一个 SQL 片段:

<sql id="Base_Column">
    id,usename,address
</sql>

然后,在其他 SQL 中,就可以引用这个变量:

<select id="getUserByIds" resultType="org.javaboy.mybatis.model.User">
    select <include refid="Base_Column" /> from user where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>

7.4.5 set

set 关键字一般用在更新中。因为大部分情况下,更新的字段可能不确定,如果对象中存在该字段的值,就更新该字段,不存在,就不更新。例如如下方法:

Integer updateUser(User user);

现在,这个方法的需求是,根据用户 id 来跟新用户的其他属性,所以,user 对象中一定存在 id,其他属性则不确定,其他属性要是有值,就更新,没值(也就是为 null 的时候),则不处理该字段。

我们结合 set 节点,写出来的 sql 如下:

<update id="updateUser" parameterType="org.javaboy.mybatis.model.User">
    update user
    <set>
        <if test="username!=null">
            username = #{username},
        </if>
        <if test="address!=null">
            address=#{address},
        </if>
        <if test="favorites!=null">
            favorites=#{favorites},
        </if>
    </set>
    where id=#{id};
</update>

喜欢这篇文章吗?扫码关注公众号【江南一点雨】【江南一点雨】专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 Attribution-NonCommercial 4.0 International