十、关联查询(多表结果映射)
当查询涉及多张表,查询结果的字段名与 POJO 属性名无法直接对应时,需要使用 <resultMap>来显式定义映射关系。MyBatis 主要通过 <association>和 <collection>标签处理。
1. 一对一关联
例如,一个 Account账户对象关联一个 User用户对象。
POJO 定义:
public class Account {
private Integer id;
private Double money;
// 一对一关联属性
private User user;
// ... getter/setter
}
Mapper XML 配置:
<resultMap id="findAccountByIdResultMap" type="com.hg.pojo.Account">
<!-- 映射 Account 自身的基本属性 -->
<id column="aid" property="id"/>
<result column="money" property="money"/>
<!-- 一对一映射:使用 association 标签
property: Account 类中的关联属性名,这里是 "user"
javaType: 该属性的全限定类名
-->
<association property="user" javaType="com.hg.pojo.User">
<!-- 映射 User 对象的属性 -->
<id column="uid" property="id"/>
<result column="username" property="username"/>
</association>
</resultMap>
2. 一对多关联
例如,一个 User用户对象拥有多个 Account账户对象。
POJO 定义:
public class User implements Serializable {
private Integer id;
private String username;
// 一对多关联属性
private List<Account> accountList;
// ... getter/setter
}
Mapper XML 配置:
<resultMap id="findUserByIdResultMap" type="com.hg.pojo.User">
<!-- 映射 User 自身的基本属性 -->
<id column="id" property="id"/>
<result column="username" property="username"/>
<!-- 一对多映射:使用 collection 标签
property: User 类中的关联集合属性名,这里是 "accountList"
ofType: 集合中泛型的全限定类名
-->
<collection property="accountList" ofType="com.hg.pojo.Account">
<id column="aid" property="id"/>
<result column="money" property="money"/>
</collection>
</resultMap>
3. 多对多关联
多对多关系通常通过中间表实现。例如,User和 Role角色之间是多对多关系,对应数据库表 user, user_role, role。
POJO 定义(以 Role 为例):
public class Role {
private Integer id;
private String roleName;
// 一对多(本质上是多对多):一个角色对应多个用户
private List<User> userList;
// ... getter/setter
}
Mapper XML 配置:
<resultMap id="findRoleByIdResultMap" type="com.hg.pojo.Role">
<!-- 映射 Role 自身的基本属性 -->
<id column="rid" property="id"/>
<result column="role_name" property="roleName"/>
<!-- 多对多映射:同样使用 collection 标签
配置方式与一对多完全相同
-->
<collection property="userList" ofType="com.hg.pojo.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
</collection>
</resultMap>
十一、MyBatis 延迟加载(懒加载)
延迟加载(Lazy Loading)是指在真正需要使用关联数据时才发起查询,而非在查询主对象时立即加载所有关联数据,以此提升性能。
配置示例:
<resultMap id="findUserByIdResultMap" type="com.hg.pojo.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<!--
property: 需要延迟加载的集合属性名,如 "accountList"
ofType: 集合中元素的类型
select: 触发延迟加载时要执行的 SQL 语句标识(Namespace + ID)
column: 传递给上述 `select` 语句的参数,通常是主查询结果中的某列(如用户id)
fetchType: 加载方式。`lazy` 表示延迟加载,`eager` 表示立即加载
-->
<collection property="accountList"
ofType="com.hg.pojo.Account"
select="com.hg.mapper.AccountMapper.findAccountByUid"
column="id"
fetchType="lazy">
</collection>
</resultMap>
说明:
-
当调用
findUserById方法时,只会执行查询用户信息的 SQL。 -
只有当代码中访问
user.getAccountList()时,MyBatis 才会执行findAccountByUid方法来查询该用户的账户列表。
十二、MyBatis 的动态 SQL
动态 SQL 允许我们根据条件灵活地拼接 SQL 语句,主要使用以下几个标签。
1. <if>标签:条件判断
用于在 SQL 片段中添加条件判断。
<if test="条件表达式">
AND column_name = #{value}
</if>
示例:
<if test="username != null and username != ''">
AND username = #{username}
</if>
2. <where>标签:替代 WHERE 关键字
-
会智能地在子元素有内容时插入
WHERE关键字。 -
会自动去除子句开头多余的
AND或OR。
SELECT * FROM user
<where>
<if test="username != null">AND username = #{username}</if>
<if test="age != null">AND age = #{age}</if>
</where>
3. <set>标签:用于 UPDATE 语句
-
会智能地在子元素有内容时插入
SET关键字。 -
会自动去除末尾多余的逗号
,。
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
4. <trim>标签:自定义字符串修剪
功能比 <where>和 <set>更强大,可以自定义前缀/后缀的添加和去除。
-
prefix:在整体内容前添加指定字符串。 -
prefixOverrides:去除整体内容开头的指定字符串。 -
suffix:在整体内容后添加指定字符串。 -
suffixOverrides:去除整体内容末尾的指定字符串。
<!-- 模拟 WHERE 标签 -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<!-- 模拟 SET 标签 -->
<trim prefix="SET" suffixOverrides=",">
...
</trim>
5. <foreach>标签:遍历集合
常用于 IN查询或批量操作。
<!--
collection: 指定要遍历的集合参数,取值可以是:
list, array, map, @Param("keyName"), 或 POJO 的属性名
item: 遍历时每个元素的别名
open: 遍历开始前添加的字符串
separator: 元素之间的分隔符
close: 遍历结束后添加的字符串
index: (可选) 在 list/array 中是索引,在 map 中是 key
-->
<foreach collection="ids" item="id" open="id IN (" separator="," close=")">
#{id}
</foreach>
6. <sql>与 <include>标签:SQL 片段复用
-
<sql>:定义可重用的 SQL 片段。 -
<include>:引用定义好的 SQL 片段。
<!-- 定义公共的 where 条件 -->
<sql id="findUserWhere">
<where>
<if test="username != null">AND username LIKE CONCAT('%', #{username}, '%')</if>
<if test="email != null">AND email = #{email}</if>
</where>
</sql>
<!-- 在查询中引用 -->
SELECT * FROM user
<include refid="findUserWhere"/>
十三、MyBatis 缓存机制
MyBatis 提供了两级缓存来提高查询效率。
1. 一级缓存
-
范围:
SqlSession级别。同一个SqlSession内部共享。 -
生命周期:默认开启,无法关闭。与
SqlSession生命周期相同,SqlSession关闭或执行了增、删、改操作后,缓存会被清空。 -
生效场景:在同一个
SqlSession中,连续执行两次相同的查询,第二次会直接从一级缓存中获取数据。
2. 二级缓存
-
范围:
Mapper级别(namespace级别)。多个SqlSession可以共享同一个 Mapper 的二级缓存。 -
生命周期:需要手动在核心配置文件或 Mapper XML 中开启。生命周期与应用同步,数据会序列化保存到硬盘或其它共享存储中。
-
开启步骤:
-
在
mybatis-config.xml中开启全局缓存(默认是开启的):<settings> <setting name="cacheEnabled" value="true"/> </settings> -
在需要开启二级缓存的 Mapper XML 文件中添加
<cache/>标签。
-
-
注意事项:
-
查询结果对应的实体类必须实现
Serializable接口。 -
只有执行了
sqlSession.commit()或sqlSession.close()后,查询结果才会被存入二级缓存。 -
执行任何增、删、改操作都会清空当前
namespace下的所有二级缓存。
-
缓存执行顺序:当发起查询时,MyBatis 会先查询二级缓存,再查询一级缓存,如果都没有命中,最后才去查询数据库。
2287



被折叠的 条评论
为什么被折叠?



