MyBatis动态SQL大全

目录

  • 一,MyBatis动态SQL介绍
    • 1,MyBatis动态SQL是做什么的?
    • 2,MyBatis的9种动态SQL标签
    • 3,动态SQL的执行原理?
  • 二,MyBatis标签
    • 1,where+if标签:条件判断
    • 2,Set标签
    • 3,choose(when,otherwise)语句
    • 4,trim
    • 5,MyBatis foreach标签
    • 6,sql,include标签
    • 7,saveOrUpdate方法

一,MyBatis动态SQL介绍

动态SQL是MyBatis的强大特性之一。在JDBC或其它类似的框架中,通常需要开发人员手动拼接SQL语句。根据不同的条件拼接不同的SQL语句是一件及其麻烦的事情,例如:拼接是要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。
动态SQL一般是根据用户输入或外部条件动态组合的SQL语句块。通过一些条件的判断,可以实现在不同情况下执行不同的SQL语句,避免了手动拼接SQL的麻烦,不过动态SQL有时候在执行性能上面不如动态SQL,而且使用不恰当,往往会在安全方面存在隐患。如SQL注入的攻击。

1,MyBatis动态SQL是做什么的?

MyBatis动态SQL可以让我们在Xml映射文件内,以标签的形式编写动态SQL,完成逻辑判断和动态拼接SQL的功能。

2,MyBatis的9种动态SQL标签

元素 作用 备注
if 判断语句 单条件分支判断
choose 相当于Java中的Switch case语句 多条件分支判断
trim,where 辅助元素 用于处理一些SQL拼装问题
foreach 循环语句 在in语句等列举条件中较常用
bind 辅助元素 拼接参数
sql 定义一个通用功能 常用include结合使用

3,动态SQL的执行原理?

原理:使用OGNL从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此来完成动态SQL的功能。

二,MyBatis标签

在使用标签前,先做一些准备工作。

  • 创建一个student表来做测试:
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`
(
    `id`          int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
    `name`        varchar(45)  DEFAULT NULL COMMENT '名称',
    `gender`      tinyint(1) DEFAULT NULL COMMENT '性别:1男性,0女性',
    `age`         int(11) DEFAULT NULL COMMENT '年龄',
    `grade_class`       varchar(45) DEFAULT NULL COMMENT '年级-班级',
    `birthday`    datetime DEFAULT NULL COMMENT '出生日期',
    `flag`        tinyint(1) NOT NULL DEFAULT 1 COMMENT '有效标志:1有效,0无效',
    `description` varchar(255) DEFAULT NULL COMMENT '描述',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 定义实体类
@Data
public class Student {
    private Integer id;
    private String name;
    private boolean gender;
    private Integer age;
    private String gradeClass;
    private Date birthday;
    private String description;
}

1,where+if标签:条件判断

if标签与java中的if语句类似,用于条件判断,使用if标签可以根据条件不同,自动拼接不同的SQL。下面是一个例子。
现在需实现下面的功能,当传入的参数中值不为空时,则将其作为查询条件,若为空,则不作为查询条件。

  • 接口
// 通过if条件查询学生信息
    List<Student> selectStudentsByIf(@Param("name") String name, @Param("gender") boolean gender,@Param("age") Integer age);
  • xml语句
    where标签会去掉第一个成立的if条件前面的and或者是or
<resultMap id="studentMap" type="com.tick.tack.manager.entity.Student">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <result column="gender" property="gender" jdbcType="TINYINT"/>
        <result column="age" property="age" jdbcType="INTEGER"/>
        <result column="grade_class" property="gradeClass" jdbcType="VARCHAR"/>
        <result column="birthday" property="birthday" jdbcType="DATE"/>
        <result column="description" property="description" jdbcType="VARCHAR"/>
resultMap>
    
<select id="selectStudentsByIf" resultMap="studentMap">
        select * from `student`
        <where>
            <if test="name!=null and name != ''">
                `name`like concat('%',#{name},'%')
            if>
            <if test="gender!=null">
                AND `gender`=#{gender}
            if>
            <if test="age!=null">
                AND `age`=#{age}
            if>
        where>
select>

2,Set标签

set标签可以用来修改

  • 接口
// 通过set修改学生信息
void updateStudentBySet(Student student);
  • xml
<update id="updateStudentBySet" parameterType="com.tick.tack.manager.entity.Student">
        update `student`
        <set>
            <if test="name != null and name !=''">`name`=#{name},if>
            <if test="gender!=null">`gender`=#{gender},if>
            <if test="age!=null">`age`=#{age},if>
            <if test="gradeClass!=null and gradeClass!=''">`grade_class`=#{gradeClass},if>
            <if test="birthday != null">`birthday`=#{birthday},if>
            <if test="description!=null and description!=''">`description`=#{description}if>
        set>
        where `id`=#{id}
update>

3,choose(when,otherwise)语句

有时候,我们不想用到所有的查询条件,只想选择其中一个,查询条件有一个满足即可,使用choose标签可以解决此类问题,类似于Java的Switch语句

  • 接口
// 通过choose查询学生信息
    List<Student> selectStudentByChoose(@Param("name") String name, @Param("gender") boolean gender,@Param("age") Integer age);
  • xml
<select id="selectStudentByChoose" resultMap="studentMap">
        select * from `student`
        <where>
            <choose>
                <when test="name!=null and name != ''">
                    `name` like concat('%',#{name},'%')
                when>
                <when test="gender!=null">
                    AND `gender`=#{gender}
                when>
                <when test="age!=null">
                    AND `age`>=#{age}
                when>
                <otherwise>
                    AND 1=1
                otherwise>
            choose>
        where>
select>

4,trim

trim标记是一个格式化的标记,可以完成set或者是where标记的功能。
通过trim改写if,where语句

  • 接口
// 通过if和trim条件查询学生信息
    List<Student> selectStudentByTrimIf(@Param("name") String name, @Param("gender") boolean gender,@Param("age") Integer age);
  • xml
    • prefix:前缀
    • prefixoverride:去掉第一个and或者是or
<select id="selectStudentByTrimIf" resultMap="studentMap">
        select * from `student`
        <trim prefix="where" prefixOverrides="and | or">
            <if test="name!=null and name !=''">
                `name` like concat('%',#{name},'%')
            if>
            <if test="gender!=null">
                AND `gender`=#{gender}
            if>
            <if test="age!=null">
                AND `age`=#{age}
            if>
        trim>
select>

通过trim改写if,set语句

// 通过trim来修改学生信息,可替换标签
void updateStudentByTrimSet(Student student);
  • xml
    • prefix:前缀
    • suffixOverrides:去掉最后一个逗号
<update id="updateStudentByTrimSet" parameterType="com.tick.tack.manager.entity.Student">
        update `student`
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null and name !=''">`name`=#{name},</if>
            <if test="gender!=null">`gender`=#{gender},</if>
            <if test="age!=null">`age`=#{age},</if>
            <if test="gradeClass!=null and gradeClass!=''">`grade_class`=#{gradeClass},</if>
            <if test="birthday != null">`birthday`=#{birthday},</if>
            <if test="description!=null and description!=''">`description`=#{description}</if>
        </trim>
        where `id`=#{id}
    </update>

通过if,trim可以实现添加功能

  • 接口
// 通过trim插入学生信息
void insertStudentByTrim(Student student);
  • xml
<insert id="insertStudentByTrim" parameterType="com.tick.tack.manager.entity.Student">
        insert into `student`
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="name != null and name !=''">`name`,if>
            <if test="gender!=null">`gender`,if>
            <if test="age!=null">`age`,if>
            <if test="gradeClass!=null and gradeClass!=''">`grade_class`,if>
            <if test="birthday != null">`birthday`=#{birthday},if>
            <if test="description!=null and description!=''">`description`if>
        trim>

        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="name != null and name !=''">#{name},if>
            <if test="gender!=null">#{gender},if>
            <if test="age!=null">#{age},if>
            <if test="gradeClass!=null and gradeClass!=''">#{gradeClass},if>
            <if test="birthday != null">#{birthday},if>
            <if test="description!=null and description!=''">#{description}if>
        trim>
    insert>

5,MyBatis foreach标签

foreach是用来对集合进行遍历,和java中的foreach功能类似。
我们可以将任何可迭代对象(如List,Set等),Map对象或者数组对象作为集合参数传递给foreach。当使用迭代对象或者数组时,index是当前迭代的序号,item的值是本次迭代获取到的元素。当使用Map对象(或者Map.Entry对象的集合)时,index是键,item是值。

通过foreach查询id在集合中的数据。

  • 接口
// 通过foreach批量查询
List<Student> selectStudentByIds(@Param("ids") List<Integer> ids);
  • xml
<select id="selectStudentByIds" resultMap="studentMap">
        select * from `student`
        <if test="ids != null and ids.size() > 0">
            where `id` in
            <foreach collection="ids" item="item" index="index" open="(" close=")" separator=",">
                #{item}
            foreach>
        if>
select>

通过foreach实现批量插入

  • 接口
// 通过foreach批量插入学生信息
void insertStudentBatch(@Param("list") List<Student> students);
  • xml
<insert id="insertStudentBatch">
        insert into `student`(`name`,`gender`,`age`,`grade_class`,`birthday`,`description`)
        values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.name},#{item.gender},#{item.age},#{item.gradeClass},#{item.birthday},#{item.description})
        foreach>
insert>

6,sql,include标签

在开发过程中会遇到许多相同的SQL,以查询功能为例,各功能要查询的参数都是一样,但是筛选条件不同,那么我们便可以将相同的部分抽取出来,用得时候直接引用即可,不用再写重复代码。


<sql id="selectStudent">
        select * from  `student`
sql>

<select id="selectStudentById" resultType="com.tick.tack.manager.entity.Student">
	
    <include refid="com.tick.tack.manager.dao.selectStudent"/>
    where 1=1
    <if test="id!=null">
        and `id`=#{id}
    if>
select>

7,saveOrUpdate方法

MyBatis Plus有提供saveOrUpdata方法,MyBatis未提供,我们自己可自己实现该功能。

  • 接口
// 插入或更新接口
void saveOrUpdate(@Param("student") Student student);

// 插入或更新接口-方便事务控制
void saveOrUpdateDuplicate(Student student);
  • xml
<insert id="saveOrUpdate" useGeneratedKeys="true"
        keyProperty="id">
    <selectKey keyProperty="count" resultType="int" order="BEFORE">
        select count(*) from `student` where `id`=#{student.id}
    </selectKey>
    <if test="count>0">
        update `student`
        set
        `name`=#{student.name},
        `gender`=#{student.gender},
        `age`=#{student.age},`grade_class`=#{student.gradeClass},`birthday`=#{student.birthday},
        `description`=#{student.description}
        where `id`=#{student.id}
    </if>
    <if test="count==0">
        insert into `student`(`name`,`gender`,`age`,`grade_class`,`birthday`,`description`)
        values (#{student.name},#{student.gender},#{student.age},
        #{student.gradeClass},#{student.birthday},#{student.description})
    </if>
</insert>

<insert id="saveOrUpdateDuplicate" parameterType="com.tick.tack.manager.entity.Student" useGeneratedKeys="true"
        keyProperty="id">
    insert into `student`(`id`,`name`, `gender`, `age`, `grade_class`, `birthday`, `description`)
    values (#{id},#{name}, #{gender}, #{age}, #{gradeClass}, #{birthday}, #{description})
    on duplicate key update
    <if test="name != null and name !=''">`name`=#{name},</if>
    <if test="gender!=null">`gender`=#{gender},</if>
    <if test="age!=null">`age`=#{age},</if>
    <if test="gradeClass!=null and gradeClass!=''">`grade_class`=#{gradeClass},</if>
    <if test="birthday != null">`birthday`=#{birthday},</if>
    <if test="description!=null and description!=''">`description`=#{description}</if>
</insert>
  • 第一种方式:先根据id做一次查询,如果能查到数据,就更新原数据;如果没查到数据,则插入新数据。
  • 第二种方式:在MySQL中,当在INSERT语句末尾指定了ON DUPLICATE KEY UPDATE,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则在出现重复值的行执行UPDATE;如果不会导致唯一值列重复问题,则插入该行。个人推荐用第二种方式,比较方便事务管理。

你可能感兴趣的:(项目开发,mybatis,sql,spring)