一,xml映射

- 随着用户的输入或外部条件的变化而变化的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效果如图:
1.1 SQL语句标签
-
select 标签
<select id="selectById" resultMap="BaseResultMap" parameterType="Object"> select * from user where id=#{id} </select> -
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> -
delete 标签
<delete id="deleteById" parameterType="Object"> delete from user where id=#{id} </delete> -
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":指定属性名,这个属性将会在插入操作完成后被赋值为数据库生成的主键值。在这个例子中,id是DemoUser对象的一个属性。
标签内属性说明:
- 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。
如下图所示:

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 标签, 如下图:

where 标签有两个作用:
- 如果标签内的if条件都不成立他就不会生成where
- 如果标签内存在多余的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 用来遍历集合,用法如下图:

- collection: 遍历的集合
- item: 遍历出来的元素
- separator: 分隔行
- open: 遍历开始前拼接的SQL片段
- close: 遍历结束后拼接的SQL片段
forEach 常见的用法:
-
批量删除
@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> -
批量插入
@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> -
批量查询
@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 语句。

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分页查询
分页查询逻辑:

SQL 语句逻辑:

需要在后端专门创建一个PageBean来存储分页查询的数据:

后端执行流程:

使用 PageHelper 进行分页查询与普通分页查询的对比:

使用 PageHelper 需要引入依赖:

