MyBatis-动态sql与高级映射

动态SQL

if标签

一般应用在多条件查询中

xml 复制代码
<select id="selectByMultipleCondition" resultType="car" parameterType="Car">
SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
FROM t_car WHERE 1=1
 /*   if标签中test属性是必须的,test属性的值是一个boolean类型的值。
    只有test属性的值为true时,if标签中的SQL片段才会被拼接到最终的SQL语句中。
      test属性中可以使用的是:
      当使用了@Param注解:test使用的是注解指定的参数名。比如@Param("brand"),那么只能使用brand;
      若没有使用@Param注解:test使用的是arg0、arg1、param1、param2
      当使用了Pojo:那么test中使用的Pojo类的属性名*/
<if test="carNum != null and carNum != ''">
    AND car_num = #{carNum}
</if>
<if test="brand != null and brand != ''">
    AND brand = #{brand}
</if>
<if test="carType != null and carType != ''">
    AND car_type = #{carType}
</if>
</select>

where标签

where标签的作用:让where子句更加动态智能,

  • 所有条件都为空时,where标签保证不会生成where子句
  • 自动去除某些条件前面多余的and或or(只能处理前面的,不能处理后面的)
xml 复制代码
<select id="selectByMultipleConditionWithWhere" resultType="car" parameterType="Car">
    SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
    FROM t_car
    <where>
        <if test="carNum != null and carNum != ''">
            AND car_num = #{carNum}
        </if>
        <if test="brand != null and brand != ''">
            AND brand = #{brand}
        </if>
        <if test="carType != null and carType != ''">
            AND car_type = #{carType}
        </if>
    </where>
</select>

trim标签

xml 复制代码
<select id="selectByMultipleConditionWithTrim" resultType="car" parameterType="Car">
    SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
    FROM t_car

  /*  prefix:加前缀   suffix:加后缀  prefixOverrides:去掉前缀   suffixOverrides:去掉后缀
    prefix="WHERE" 表示在trim标签所有你内容的前面加上WHERE关键字。
    suffixOverrides="AND |OR " 表示如果trim标签中的内容以AND或者OR结尾,那么就去掉这个AND或者OR。
      */
    <trim prefix="WHERE" suffixOverrides="AND |OR ">
        <if test="carNum != null and carNum != ''">
             car_num = #{carNum} AND
        </if>
        <if test="brand != null and brand != ''">
             brand = #{brand} AND
        </if>
        <if test="carType != null and carType != ''">
             car_type = #{carType}
        </if>
    </trim>
</select>

set标签

主要使用在update语句当中,用来生成set关键字,同时去掉最后多余的","

比如我们只更新提交不为空的字段,如果提交的数据是空或者"",那么不更新这个字段。

xml 复制代码
<update id="updateBySet" parameterType="Car">
    UPDATE t_car
    <set>
        <if test="carNum != null and carNum != ''">
            car_num = #{carNum},
        </if>
        <if test="brand != null and brand != ''">
            brand = #{brand},
        </if>
        <if test="guidePrice != null">
            guide_price = #{guidePrice},
        </if>
        <if test="produceTime != null">
            produce_time = #{produceTime},
        </if>
        <if test="carType != null and carType != ''">
            car_type = #{carType}
        </if>
    </set>
    WHERE id = #{id}
</update>

choose when otherwise 标签

这三个标签等同于 if... else if ... else ... 只有一个分支会被执行。

xml 复制代码
<select id="selectByChoose" resultMap="car">
    select * from t_car
    <where>
        <choose>
            <when test="carNum != null and carNum != ''">
                car_num = #{carNum}
            </when>
            <when test="brand != null and brand != ''">
                brand = #{brand}
            </when>
            <otherwise>
                1=1
            </otherwise>
        </choose>
    </where>
</select>

foreach标签

循环数组或集合时使用

批量删除

xml 复制代码
<!--对应的接口方法: int deleteByIds(@Param("ids") Long[] ids);-->
<delete id="deleteByIds" >
    DELETE FROM t_car WHERE id IN
    /*collection:指定要遍历的集合,可以是list、set、array等。
    item:代表数组或集合中的元素
    open:指定foreach标签生成的SQL片段的开头
    separator:指定foreach标签生成的SQL片段之间的分隔符
    close:指定foreach标签生成的SQL片段的结尾
     这个foreach标签会将传入的list集合中的每个元素都用#{id}占位符替换,并且用逗号分隔开来,最终生成一个类似于(id1,id2,id3)这样的SQL片段。
     这样就可以实现批量删除的功能了。
     注意:在使用foreach标签时,传入的参数类型必须是一个集合类型,否则会报错。*/
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

批量插入

xml 复制代码
<!--对应的接口方法: int insertBatch(@Param("list") List<Car> carList);-->
<insert id="insertBatch" parameterType="list">
    INSERT INTO t_car (id, car_num, brand,guide_price,produce_time,car_type)
    VALUES
    <foreach collection="list" item="car" separator=",">
        (null, #{car.carNum}, #{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
    </foreach>
</insert>

sql标签和include标签

sql标签用来声明sql片段。

include标签用来将声明的sql片段包含到某个sql语句当中。

主要为了代码复用。

xml 复制代码
<sql id="carColumns">
    id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
</sql>
<select id="selectAllCarWithSql" resultType="car">
    SELECT
    <include refid="carColumns"/>
    FROM t_car
</select>

MyBatis的高级映射及延迟加载

有一个业务场景,有学生表和班级表。一个学生对应一个班级,一个班级包含多个学生。那么学生与班级的关系就是多对一。多的一方是Student 、一的一方是Clazz。

怎么分主表和子表?

原则:谁在前谁就是主表。

多对一:多在前,多就是主表

一对多:一在前,一就是主表

多对一

多对一的映射方式包括三种:

  1. 一条sql语句,级联属性映射
  2. 一条sql语句,association
  3. 两条sql语句,分步查询(优点1.可复用2.支持懒加载)

第一种方式:级联属性映射

Student类设计如下,添加一个班级类的属性,表示学生关联的班级对象。

java 复制代码
public class Student {
    private Integer sid;
    private String sname;
    private Clazz clazz; // 班级对象

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", clazz=" + clazz +
                '}';
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    public Student(Integer sid, String sname) {
        this.sid = sid;
        this.sname = sname;
    }

    public Student() {
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }
}

StudentMapper.xml设计如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ali.mapper.StudentMapper">
    <!--一条sql语句,级联属性映射-->
    <resultMap id="studentResultMap" type="Student">
        <id property="sid" column="sid"/>
        <result property="sname" column="sname"/>
        <result property="clazz.cid" column="cid"/>
        <result property="clazz.cname" column="cname"/>
    </resultMap>
    
    <!--对应的Mapper接口方法是:Student getStudentById(int id);-->
    <select id="getStudentById" resultMap="studentResultMap">
        select s.sid, s.sname, c.cid, c.cname
        from t_student s
        left join t_clazz c on s.cid = c.cid
        where s.sid = #{sid}
    </select>
</mapper>

第二种方式:一条sql语句,association

Pojo类不用修改,只是resultMap内容做了修改,StudentMapper.xml设计如下:

xml 复制代码
<resultMap id="studentResultMapAssociation" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <!--association标签表示一对一的关系,
    property属性表示要映射的Pojo类的属性名,(Stusrnt类的clazz属性)
    javaType属性表示这个属性的类型,
    column属性表示要使用哪个列的值来查询关联对象-->
    <association property="clazz" javaType="Clazz">
        <id property="cid" column="cid"/>
        <result property="cname" column="cname"/>
    </association>
</resultMap>

   <!--对应的Mapper接口方法是:Student getStudentById(int id);-->
    <select id="getStudentById" resultMap="studentResultMapAssociation">
        select s.sid, s.sname, c.cid, c.cname
        from t_student s
        left join t_clazz c on s.cid = c.cid
        where s.sid = #{sid}
    </select>

第三种方式:两条sql语句,分步查询

第一步查询,StudentMapper.xml设计如下:

xml 复制代码
<resultMap id="studentResultMapStep" type="Student">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <!--association标签表示一对一的关系,
    property属性表示要映射的Pojo类的属性名,(Stusrnt类的clazz属性)
    column属性表示要使用哪个列的值来查询关联对象
    select指定第二步sql语句的id-->
    <association property="clazz" column="cid"
                 select="com.ali.mapper.ClazzMapper.getClazzByIdStep2"/>
</resultMap>

<select id="getStudentByIdStep1" resultMap="studentResultMapStep">
    select s.sid, s.sname, s.cid
    from t_student s
    where s.sid = #{sid}
</select>

第二步查询,ClazzMapper.xml设计如下:

xml 复制代码
<!--分步查询第二步:根据cid获取班级信息-->
<select id="getClazzByIdStep2" resultType="Clazz">
    select cid, cname
    from t_clazz
    where cid = #{cid}
</select>

这种方式执行了2次sql查询。

多对一延迟加载

分步查询的优点:可复用性强。

延迟加载的核心就是:用的时候再查询,不用的时候不查询。

作用是提高性能。尽可能的不查或者少查。

当查询返回student对象时,此还没有执行第二步查询,当调用student.getClazz()方法时,才执行第二步查询。

在mybatis当中,怎么开启延迟加载?

在association标签种添加fetchType="lazy",这是一种局部设置,只针对当前的association关联的sql语句起作用。

那怎么开启全局的懒加载呢?

在mybatis-config.xml文件中设置如下:

xml 复制代码
<settings>
      <!--延迟加载的全局开关,默认关闭。
        所有的分步查询,都采用延迟加载。
        实际开发中建议开启。-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

在开启全局延迟加载的情况下,如果某个分步查询不需要开启延迟加载怎么办?

在对应的association标签中添加fetchType="eager",就可以关闭这个查询的延迟加载。

一对多

一个班级对应多个学生

一对多的映射实现方式有两种:

  1. collection
  2. 分步查询

第一种方式:collection

班级类设计如下:

java 复制代码
public class Clazz {
    private Integer cid;
    private String cname;
    private List<Student> students; // 一个班级有多个学生

    @Override
    public String toString() {
        return "Clazz{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", students=" + students +
                '}';
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public Clazz() {
    }

    public Clazz(Integer cid, String cname) {
        this.cid = cid;
        this.cname = cname;
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }
}

ClazzMapper.xml文件设计如下:

xml 复制代码
<resultMap id="clazzResultMap" type="Clazz">
    <id property="cid" column="cid"/>
    <result property="cname" column="cname"/>
    <!--collection标签表示一对多的关系,
    property属性表示要映射的Pojo类的属性名,(Clazz类的students属性)
    ofType属性表示集合当中的元素类型,-->
    <collection property="students" ofType="Student" >
        <id property="sid" column="sid"/>
        <result property="sname" column="sname"/>
    </collection>
</resultMap>

<select id="getClazzByCollection"  resultMap="clazzResultMap">
    select cid, cname,s.sid, s.sname
    from t_clazz c
    left join t_student s on c.cid = s.cid
    where c.cid =  #{cid}
</select>

第二种方式:分步查询

第一步查询:ClazzMapper.xml文件设计如下:

xml 复制代码
<resultMap id="clazzResultMapStep" type="Clazz">
    <id property="cid" column="cid"/>
    <result property="cname" column="cname"/>
    <!--collection标签表示一对多的关系,
    property属性表示要映射的Pojo类的属性名,(Clazz类的students属性)
    ofType属性表示集合当中的元素类型,
    column="cid" 表示根据cid查询学生信息-->
    <collection property="students" ofType="Student" column="cid"
                select="com.ali.mapper.StudentMapper.getStudentsByClazzId"/>
</resultMap>

<select id="getClazzByIdStep1" resultMap="clazzResultMapStep">
    select cid, cname
    from t_clazz
    where cid = #{cid}
</select>

第二步查询:StudentMapper.xml设计如下:

xml 复制代码
<select id="getStudentsByClazzIdStep2" resultType="student">
    select sid, sname
    from t_student
    where cid = #{cid}
</select>

一对多的延迟加载和多对一的延迟加载开启方式一致。

相关推荐
后端AI实验室4 小时前
我把同一个需求分别交给初级程序员、高级程序员和AI,结果让我沉默了
java·ai
sTone873754 小时前
web后端开发概念: VO 和 PO
java·后端·架构
SimonKing5 小时前
JetBrains+Qoder变身Agentic 编码平台,媲美Cursor、Trae等AI编程平台
java·后端·程序员
Seven975 小时前
NIO:解开非阻塞I/O高并发编程的秘密
java
华仔啊17 小时前
千万别给数据库字段加默认值 null!真的会出问题
java·数据库·后端
老赵全栈实战20 小时前
【每日一技MyBatis trim标签核心用法
java·mybatis·orm
beata20 小时前
Java基础-19:Java 死锁深度解析:从原理、检测到预防与实战指南
java·前端
吾日三省Java1 天前
Spring Cloud架构下的日志追踪:传统MDC vs 王炸SkyWalking
java·后端·架构
爱玩泥巴的小t1 天前
new Thread().start()底层做了什么?
java