Mybatis进阶

wdadwa
2
2026-04-01

一,xml映射

2953321-20231018133831847-58775772.png

  • 随着用户的输入或外部条件的变化而变化的SQL语句,我们称为 动态SQL。
  • xml映射SQL主要是用来配置复杂的,动态的SQL语句。
  • resultType是单条记录封装的返回值的全类名。

一般我们会通过在配置文件中指定扫描 mapper.xml 路径

mybatis:
    # 搜索指定包别名
    typeAliasesPackage: com.gtzc.**.domain
    # 配置mapper的扫描,找到所有的mapper.xml映射文件
    mapperLocations: classpath*:mapper/**/*Mapper.xml
    # 加载全局的配置文件
    configLocation: classpath:mybatis/mybatis-config.xml

效果如图:

2953321-20240702114955012-1968561087.png

1.1 SQL语句标签

  1. select 标签

    <select id="selectById" resultMap="BaseResultMap" parameterType="Object">
        select * from user where id=#{id}
    </select>
    
  2. update 标签

    <update id="update" parameterType="Object">
    	update user set 
    		<if test="name != null"> NAME = #{name},</if>
    		<if test="sex != null"> SEX = #{sex} </if>
    	where ID = #{id}
    </update>
    
  3. delete 标签

    <delete id="deleteById" parameterType="Object">
        delete from user where id=#{id}
    </delete>
    
  4. insert 标签

    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    	insert into user name=#{name}
    </insert>
    

    useGeneratedKeys 和 keyProperty 是 insert 标签独有的属性

    • useGeneratedKeys=true:告诉 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来获取由数据库自动生成的主键值。
    • keyProperty="id":指定属性名,这个属性将会在插入操作完成后被赋值为数据库生成的主键值。在这个例子中,idDemoUser 对象的一个属性。

标签内属性说明:

  • parameterType:parameterType: 传入给语句的参数类型。
  • id:唯一标识需要和mapper文件里面的方法名保持一致。

1.2 接口方法传入多个参数解决方法

在上述标签中,我们的接口方法都只传入了一个参数,然后用 parameterType 来指定我们的值,但当我们的接口方法出现多个参数时解决方法:

1.2.1 使用Map

接口:

public interface UserMapper {
    User selectUser(@Param("id") int id, @Param("name") String name);
}

xml 配置:

<select id="selectUser" parameterType="map" resultType="com.example.User">
    SELECT * FROM users WHERE id = #{id} AND name = #{name}
</select>

1.2.2 使用自定义对象

就是将多个参数新建一个对象来封装起来。

1.2.3 使用 @Param 注解

如果使用 MyBatis 3.4.0 及以上版本,可以直接在方法参数上使用 @Param 注解

public interface UserMapper {
    User selectUser(@Param("id") int id, @Param("name") String name);
}
<select id="selectUser" resultType="com.example.User">
    SELECT * FROM users WHERE id = #{id} AND name = #{name}
</select>

1.3 if标签

用于判断条件是否成立。使用 test 属性进行条件判断,如果条件为 true,则拼接 SQL。

如下图所示:

2953321-20231018141935361-828694720.png

1.4 choose标签

<choose> 标签类似于 Java 中的 switch 语句,用于在多个条件中选择一个执行分支。它可以包含多个 <when> 和一个 <otherwise> 标签,每个 <when> 标签表示一个条件分支,而 <otherwise> 标签表示默认分支。

<select id="selectUser" resultType="User">
    SELECT *
    FROM user
    <where>
        <choose>
            <when test="name != null">
                AND name = #{name}
            </when>
            <when test="age != null">
                AND age = #{age}
            </when>
            <otherwise>
                AND id = #{id}
            </otherwise>
        </choose>
    </where>
</select>

1.5 where标签

上述案例中, 存在 bug, 当第一个条件 name 为 null 时会 SQL 会拼接成错误的 SQL:

select * from emp where and gender = 1 order by update_time desc;

会多出一个 and 导致 SQL 语法错误,解决方法就是加上 where 标签, 如下图:
2953321-20231018142343817-622720895.png

where 标签有两个作用:

  1. 如果标签内的if条件都不成立他就不会生成where
  2. 如果标签内存在多余的and或者or会自动去除掉他.

1.6 Set标签

Set 标签和 where 标签的作用都是为了防止因为 if 标签导致错误的 SQL 拼接。

set 标签会去除多余的逗号即

update emp set id=1,name='zhangsan';

假设这个是动态 SQL 生成的,那么在 if 判断时可能会产生多余的逗号,这个时候就使用 set 标签包裹即可。

1.7 trim标签

trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号,或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。trim 语法格式如下。

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>

属性解释:

  • prefix:给 sql 语句拼接的前缀

  • suffix:给 sql 语句拼接的后缀

  • **prefixOverrides:** 去除 sql 语句前面的关键字或者字符,该关键字或者字符由 prefixOverrides 属性指定

    假设该属性指定为 "AND",当 sql 语句的开头为 "AND",trim 标签将会去除该 "AND"

  • suffixOverrides:去除 sql 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定

trim 标签常见用法:

    <insert id="insertDsMatirailPurchaseRegister" parameterType="DsMatirailPurchaseRegister">
        insert into ds_matirail_purchase_register
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="purchaseId != null">purchase_id,</if>
            <if test="purchaseObjects != null">purchase_objects,</if>
         </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="purchaseId != null">#{purchaseId},</if>
            <if test="purchaseObjects != null">#{purchaseObjects},</if>
        </trim>
    </insert>

1.8 foreach标签

foreach 用来遍历集合,用法如下图:
2953321-20231018143052160-1735373121.png

  • collection: 遍历的集合
  • item: 遍历出来的元素
  • separator: 分隔行
  • open: 遍历开始前拼接的SQL片段
  • close: 遍历结束后拼接的SQL片段

forEach 常见的用法:

  1. 批量删除

    @Mapper
    public interface DemoDao {
        int deleteByIds(@Param("ids") List<Integer> ids);
    }
    
    <delete id="deleteByIds">
        DELETE FROM DemoUser WHERE id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
    
  2. 批量插入

    @Mapper
    public interface DemoDao {
        int insertUsers(@Param("users") List<DemoUser> users);
    }
    
    <insert id="insertUsers">
        INSERT INTO DemoUser (name, age) VALUES 
        <foreach collection="users" item="user" separator=",">
            (#{user.name}, #{user.age})
        </foreach>
    </insert>
    
  3. 批量查询

    @Mapper
    public interface DemoDao {
        List<DemoUser> selectByIds(@Param("ids") List<Integer> ids);
    }
    
    <select id="selectByIds" resultType="com.example.DemoUser">
        SELECT id, name, age
        FROM DemoUser
        WHERE id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>
    

1.9 SQL和include标签

sql 标签和 include 标签是搭配使用的,sql 标签用来将重复内容的 sql 抽取出来,include 负责调用这个 sql 语句。

2953321-20231018143259501-1946865960.png

1.10 resultMap标签

1.10.1 resultMap标签基础使用

作用:将 SQL 语句的查询返回结果和 java 对象绑定。

    <resultMap type="MmDevice" id="MmDeviceResult">
        <result property="deviceId"    column="device_id"    />
        <result property="groupId"    column="group_id"    />
        <result property="orderId"    column="order_id"    />
        <result property="goodsId"    column="goods_id"    />
        <result property="goodsStyle"    column="goods_style"    />
        <result property="updateTime"    column="update_time"    />
        <result property="status"    column="status"    />
        <result property="operateTime"    column="operate_time"    />
    </resultMap>
  • type 对应的java 类的类名

  • id 是给 select 语句等需要返回对象的 resultMap 属性使用的

例如此时可以给 select 标签使用:

<select id="selectMmDeviceByDeviceId" parameterType="Long" resultMap="MmDeviceResult">
        select device_id, group_id, order_id, goods_id, goods_style, update_time, status, operate_time from mm_device where device_id = #{deviceId}
</select>

此时 resultMap 对应的就是 resultMap 的 id。

属性解释:

  • property对应的是 java 类里面的属性名

  • column对应的是数据库里面字段名

    需要注意,resultType 和 resultMap 不能同时使用!

1.10.1 resultMap的继承、复用、嵌套

** 继承:** 继承已存在的 resultMap 标签进行扩展

举例:

  • 先有如下文件

    public class User {
        private int id;
        private String username;
        private String firstName;
        // Getter 和 Setter 略
    }
    
  • xml 的写法

        <!-- 基础的 resultMap 定义 -->
        <resultMap id="BaseUserResultMap" type="User">
            <id property="id" column="id" />
            <result property="username" column="username" />
            <result property="email" column="email" />
        </resultMap>
    
        <!-- 继承 BaseUserResultMap 的 resultMap,并增加一个额外的字段映射 -->
        <resultMap id="ExtendedUserResultMap" type="User" extends="BaseUserResultMap">
            <result property="firstName" column="first_name" />
        </resultMap>
    

复用: 跨 mapper 文件引用现存的 resultMap 标签

假设我们有两个不同的 XML 文件,分别定义了不同的 resultMap,我们想要在一个文件中引用另一个文件中定义的 resultMap

  • File1

        <!-- File1.xml 中定义的 resultMap -->
        <resultMap id="BaseUserResultMap" type="User">
            <id property="id" column="id" />
            <result property="username" column="username" />
            <result property="email" column="email" />
        </resultMap>
    
    
  • File2

        <!-- File2.xml 中引用 File1.xml 中的 resultMap -->
        <resultMap id="ExtendedUserResultMap" type="User">
            <!--通过这个resultMap refild来实现复用-->
            <resultMap refid="BaseUserResultMap" />
    
            <result property="firstName" column="first_name" />
            <result property="lastName" column="last_name" />
        </resultMap>
    

也可以跨文件复用 sql 标签的内容

    <!-- File1.xml 中定义的 SQL 片段 -->
    <sql id="selectFields">
        id, username, email
    </sql>
    <!-- File2.xml 中引用 File1.xml 中的 SQL 片段 -->
    <select id="selectUsers" resultMap="ExtendedUserResultMap">
        SELECT
        <include refid="selectFields"/>
        FROM users
    </select>

** 嵌套:** 指在一个 resultMap 中嵌套另一个 resultMap<collection> 元素来处理数据库查询结果中的复杂对象关系。这种技术使得可以将多个数据库表中的数据映射到一个复杂的 Java 对象中,或者处理一对多关系的数据

  • 嵌套单个对象

    public class User{
        private int id;
        private String username;
        private String email;
        private UserDetails details;
    }
    private class UserDetails{
        private int id;
        private String firstName;
        private String lastName;
        private String address;
    }
    
        <!-- 定义 user 表的 resultMap -->
        <resultMap id="UserResultMap" type="User">
            <id property="id" column="id" />
            <result property="username" column="username" />
            <result property="email" column="email" />
            <!-- 嵌套 user_details 表的 resultMap -->
            <!--property对应的是java类中属性名,javaType对应的是java类中类名,resultMap对应下放的id-->
            <association property="details" javaType="UserDetails" resultMap="UserDetailsResultMap" />
        </resultMap>
    
        <!-- 定义 user_details 表的 resultMap -->
        <resultMap id="UserDetailsResultMap" type="UserDetails">
            <id property="id" column="id" />
            <result property="firstName" column="first_name" />
            <result property="lastName" column="last_name" />
            <result property="address" column="address" />
        </resultMap>
    
  • 嵌套集合对象

    public class User{
        private int id;
        private String username;
        private String email;
        //就是嵌套这个
        private List<Order> orders;
    }
    private class Order{
        private int id;
        private String orderNumber;
        private String orderDate;
    }
    
        <!-- 定义 user 表的 resultMap,嵌套 order 表的 collection -->
        <resultMap id="UserOrdersResultMap" type="User">
            <id property="id" column="id" />
            <result property="username" column="username" />
            <result property="email" column="email" />
            <!-- 嵌套 order 表的 collection -->
             <!--property对应的是java类中属性名,ofType对应的是java类中类名,resultMap对应下放的id-->
            <collection property="orders" ofType="Order" resultMap="OrderResultMap" />
        </resultMap>
    
        <!-- 定义 order 表的 resultMap -->
        <resultMap id="OrderResultMap" type="Order">
            <id property="id" column="id" />
            <result property="orderNumber" column="order_number" />
            <result property="orderDate" column="order_date" />
        </resultMap>
    

嵌套 + 继承复合写法:一般用于主子表的关系映射

public class User{
    private int id;
    private String username;
    private String email;
    //就是嵌套这个
    private List<Order> orders;
}
private class Order{
    private int id;
    private String orderNumber;
    private String orderDate;
}
    <!--主表映射-->
	<resultMap type="User" id="UserResult">
        <result property="id"    column="id"    />
		<result property="username"    column="username"    />
       <result property="email"    column="email"    />
    </resultMap>
	<!--子表映射-->
    <resultMap type="Order" id="OrderResult">
        <result property="id"    column="sub_id"    />
        <result property="orderNumber"    column="sub_order_number"    />
         <result property="orderDate"    column="sub_order_date"    />
    </resultMap>

	<!--type是主表的type,继承的值也是主表的resultMap的id,主子表查询的resultMap就采用这个的id-->
    <resultMap id="OrderExtendData" type="User" extends="UserResult">
        <!-- resultMap对应的是子表的resultMap的id-->
        <!-- notNullColumn代表不能为null的字段-->
        <!-- javaType代表的是List集合-->
        <!-- property对应的是子表在主表里面的属性名 -->
        <collection property="orders"
                    notNullColumn="sub_id" javaType="java.util.List"
                    resultMap="OrderResult" />
    </resultMap>

二,PageHelper分页查询

分页查询逻辑:
2953321-20231018151202474-2049000303.png

SQL 语句逻辑:
2953321-20231018151050076-379326979.png

需要在后端专门创建一个PageBean来存储分页查询的数据:
2953321-20231018151249941-499116660.png

后端执行流程:
2953321-20231018151544262-1282411798.png

使用 PageHelper 进行分页查询与普通分页查询的对比:
2953321-20231018152136677-1196074509.png

使用 PageHelper 需要引入依赖:
2953321-20231018152105053-516752993.png

动物装饰