(五) - 动态SQL

使用动态 SQL 可以简化代码的开发, 减少开发者的工作量, 程序可以自动根据业务参数来决定 SQL 的组成.

MyBatis 采用一系列标签来实现动态 SQL: 

 if, where, choose, when, trim, set, foreach 等.

举例说明:

现在我们想通过对象来查询数据

创建接口文件:

public interface DataRepository {
    public Student dynamicFind(Student student);
}

创建 mapper 文件:

<mapper namespace="com.ryan.repository.DataRepository">

    <select id="dynamicFind" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
        select * from student where id=#{id} and name=#{name} and phoneNumber=#{phoneNumber};
    </select>
</mapper>

测试文件:

public class Test {

    public static void main(String[] args) {
        InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        DataRepository dataRepository = sqlSession.getMapper(DataRepository.class);

        //创建查询条件对象
        Student student = new Student();
        student.setId(4399);
        student.setName("Batman");
        student.setPhoneNumber(666333);

        Student stu = dataRepository.dynamicFind(student);
        System.out.println(stu);
        sqlSession.close();

    }
}

以上是对象的每一个属性都指明了的, 自然可以查出来:

 

 但在实际开发中, 我们可能需要只用其中一个或多个属性去查询(比如用 id 或姓名去查询学生信息), 此时这套 sql 就不能用了 (因为 and 是与关系, 其中一个条件不成立则整个条件不成立):

 

 对每一种查询条件都写一个对应 sql 是不现实的, 因为可能的条件太多了, 我们需要一个动态的 sql 可以适用于这一类查询, 有如下几种解决方法:

1. if 标签

  if 标签可以自动根据表达式的结果来决定是否将对应的语句添加到 SQL 中, 如果条件不成立则不添加, 如果条件成立则添加. 修改 sql 如下:

    <select id="dynamicFind" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
        select * from student where
        <if test="id != 0">
            id=#{id}
        </if>
        <if test="name != null">
            and name=#{name}
        </if>
        <if test="phoneNumber != 0">
            and phoneNumber=#{phoneNumber};
        </if>

    </select>

再次运行:

 

 2. where 标签

  使用 if 标签很容易遇到一个问题, 即改动条件导致的语法问题, 如下例, 如果我们把第一个条件置为空, 那么就会出问题:

 

 使用 where 标签可以解决这个问题. where 标签可以自动判断是否要删除

 语句块中的 and 关键字, 如果检测到 where 直接跟 and 拼接, 则自动删除 and, 通常情况下 if 和 where 结合起来使用. 修改 sql:

    <select id="dynamicFind" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
        select * from student
        <where>
            <if test="id != 0">
                id=#{id}
            </if>
            <if test="name != null">
                and name=#{name}
            </if>
            <if test="phoneNumber != 0">
                and phoneNumber=#{phoneNumber};
            </if>
        </where>
    </select>

重新运行:

 

3. choose, when 标签

  作用与 if 类似.

    <select id="dynamicFind" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
        select * from student
        <where>
            <choose>
                <when test="id != 0">
                    id = #{id}
                </when>
                <when test="name != null">
                    name = #{name}
                </when>
                <when test="phoneNumber != 0">
                    phoneNumber = #{phoneNumber}
                </when>
            </choose>
        </where>
    </select>

 

4. trim 标签

  trim 标签中的 prefix 和 suffix 属性会被用于生成实际的 SQL 语句, 回合标签内部的语句进行拼接, 如果语句前后出现了 prefixOverrides 或者 suffixOverrides 属性中指定的值, MyBatis 框架会自动将其删除. 如下 sql 所示, 语句可以正常运行, 因为 where 和 and 联系出现, and 被删除:

    <select id="dynamicFind" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
        select * from student
        <trim prefix="where" prefixOverrides="and">
            <if test="id!=0">
                and id = #{id}
            </if>
            <if test="name!=null">
                and name = #{name}
            </if>
            <if test="phoneNumber!=0">
                and phoneNumber = #{phoneNumber}
            </if>
        </trim>
    </select>

运行:

 

5. set 标签

  set 标签用于 update 操作, 会自动根据参数选择生成 SQL 语句. 

  如下例所示, 当我们修改数据时, 如果我们只需要修改一个属性, 其实底层是把所有属性重新赋值一遍的:

    <update id="update" parameterType="com.ryan.javaClass.Student">
        update student set id=#{id}, name=#{name}, phoneNumber=#{phoneNumber} where id=#{id};
    </update>
    public static void main(String[] args) {
        InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        DataRepository dataRepository = sqlSession.getMapper(DataRepository.class);

        //创建查询条件对象
        Student student = new Student();
        student.setId(4399);

        Student stu = dataRepository.dynamicFind(student);
        System.out.println(stu);

        student.setName("Spiderman");
        System.out.println(dataRepository.update(student));

        sqlSession.close();
    }

运行:

 这样会导致资源的浪费, 为了解决这个问题, 我们需要用到 set 标签, 修改 sql 如下:

    <update id="update" parameterType="com.ryan.javaClass.Student">
        update student
        <set>
            <if test="id!=0">
                id=#{id},
            </if>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="phoneNumber!=0">
                phoneNumber=#{phoneNumber}
            </if>
        </set>
        where id=#{id};
    </update>

重新运行:

 

6.  foreach 标签

   foreach 标签可以迭代生成一系列值, 这个标签主要用于 SQL 的 in 语句.

为对象添加一个 List 属性:

@Data
public class Student {
    private long id;
    private String name;
    private long phoneNumber;
    private List<Integer> ids;

    public Student(long id, String name, long phoneNumber, List<Integer> ids) {
        this.id = id;
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.ids = ids;
    }
...

在接口中定义好方法后, 添加 SQL 如下: 对照原语句,

collection 值为属性名,

open 值为 where 到迭代元素前之间的内容,

close 值为迭代元素后面的内容,

id 值为迭代元素对象名,

separator 值为分隔符

    <select id="findByIds" parameterType="com.ryan.javaClass.Student" resultType="com.ryan.javaClass.Student">
#         select * from student where id in (5876, 5877);
        select * from student
        <where>
            <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

测试:

    public static void main(String[] args) {
        InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        DataRepository dataRepository = sqlSession.getMapper(DataRepository.class);

        //创建查询条件对象
        Student student = new Student();

        List<Integer> list = new ArrayList<Integer>();
        list.add(5876);
        list.add(5877);

        student.setIds(list);

        List<Student> stu =  dataRepository.findByIds(student);
        System.out.println(stu);
        sqlSession.close();
    }

运行:

 

posted @ 2021-02-05 18:18  山下明明子  阅读(87)  评论(0编辑  收藏  举报