MyBatis映射关系处理详解
2025/9/17大约 7 分钟
MyBatis映射关系处理详解
前置知识
在开始本教程之前,建议您已经完成:
- MyBatis基础入门教程
- MyBatis核心配置详解
- MyBatis增删改查操作
- 了解数据库关系模型
ResultMap 基础使用
字段和属性映射关系
当数据库字段名和Java实体类属性名不一致时,可以使用resultMap进行自定义映射:
// 员工实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Integer eid; // 对应数据库字段 eid
private String empName; // 对应数据库字段 emp_name
private Integer age; // 对应数据库字段 age
private String sex; // 对应数据库字段 sex
private String email; // 对应数据库字段 email
}
<!-- 自定义映射关系 -->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
<!-- 使用resultMap -->
<select id="getAllEmp" resultMap="empResultMap">
select * from t_emp
</select>
处理字段名不一致的三种方式
方式一:使用resultMap(推荐)
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
方式二:字段起别名
<select id="getAllEmp" resultType="Emp">
select eid,emp_name empName,age,sex,email from t_emp
</select>
方式三:开启驼峰命名转换
在核心配置文件中设置:
<settings>
<!-- 将表中字段的下划线自动转换为驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
多对一映射处理
实体类设计
// 部门实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer did;
private String deptName;
}
// 员工实体类(包含部门信息)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Integer eid;
private String empName;
private Integer age;
private String sex;
private String email;
private Dept dept; // 多对一关系
}
方式一:级联方式处理映射关系
<resultMap id="empAndDeptResultMapOne" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<select id="getEmpAndDept" resultMap="empAndDeptResultMapOne">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
方式二:使用association处理映射关系
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
association标签说明
property
:需要处理多对一的映射关系的属性名javaType
:该属性的类型
方式三:分步查询
第一步:查询员工信息
// EmpMapper接口
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.example.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"></association>
</resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
select * from t_emp where eid = #{eid}
</select>
第二步:查询部门信息
// DeptMapper接口
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
<resultMap id="EmpAndDeptByStepTwoResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</resultMap>
<select id="getEmpAndDeptByStepTwo" resultMap="EmpAndDeptByStepTwoResultMap">
select * from t_dept where did = #{did}
</select>
分步查询说明
select
:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)column
:设置分步查询的条件
一对多映射处理
实体类设计
// 部门实体类(包含员工列表)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer did;
private String deptName;
private List<Emp> emps; // 一对多关系
}
// 员工实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private Integer eid;
private String empName;
private Integer age;
private String sex;
private String email;
}
方式一:使用collection
<resultMap id="DeptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="DeptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
</select>
collection标签说明
property
:需要处理一对多的映射关系的属性名ofType
:表示该属性对应的集合中存储的数据的类型
方式二:分步查询
第一步:查询部门信息
// DeptMapper接口
Dept getDeptAndEmpByStepOne(@Param("did") Integer did);
<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.example.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"></collection>
</resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="DeptAndEmpByStepOneResultMap">
select * from t_dept where did = #{did}
</select>
第二步:根据部门id查询部门中的所有员工
// EmpMapper接口
List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where did = #{did}
</select>
延迟加载
延迟加载配置
分步查询的优点是可以实现延迟加载,但必须在核心配置文件中设置全局配置信息:
<settings>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
延迟加载测试
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp.getEmpName());
// 此时只执行查询emp的SQL语句
System.out.println("----------------");
System.out.println(emp.getDept());
// 此时才会执行查询dept的SQL语句
}
fetchType属性
当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果:
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.example.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy"></association>
</resultMap>
fetchType属性说明
lazy
:延迟加载eager
:立即加载
数据库表结构
-- 部门表
CREATE TABLE t_dept (
did INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(50) NOT NULL
);
-- 员工表
CREATE TABLE t_emp (
eid INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(50) NOT NULL,
age INT,
sex VARCHAR(10),
email VARCHAR(100),
did INT,
FOREIGN KEY (did) REFERENCES t_dept(did)
);
-- 插入测试数据
INSERT INTO t_dept VALUES (1, '研发部'), (2, '市场部'), (3, '人事部');
INSERT INTO t_emp VALUES
(1, '张三', 25, '男', 'zhangsan@example.com', 1),
(2, '李四', 30, '女', 'lisi@example.com', 1),
(3, '王五', 28, '男', 'wangwu@example.com', 2),
(4, '赵六', 32, '女', 'zhaoliu@example.com', 3);
映射关系总结
多对一映射
多对一映射方式选择
- 级联方式:简单直接,适合简单查询
- association方式:结构清晰,推荐使用
- 分步查询:支持延迟加载,适合复杂查询
一对多映射
一对多映射方式选择
- collection方式:一次性查询,适合数据量小的情况
- 分步查询:支持延迟加载,适合数据量大的情况
延迟加载使用场景
- 数据量大:避免一次性加载大量数据
- 关联查询复杂:减少不必要的查询
- 性能要求高:按需加载,提高响应速度
最佳实践
1. 合理选择映射方式
<!-- 简单查询使用association -->
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
<!-- 复杂查询使用分步查询 -->
<association property="dept"
select="com.example.mybatis.mapper.DeptMapper.getDeptByStep"
column="did"
fetchType="lazy"></association>
2. 配置延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
3. 使用resultMap提高可维护性
<!-- 定义可复用的resultMap -->
<resultMap id="BaseEmpResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
<!-- 继承基础resultMap -->
<resultMap id="EmpWithDeptResultMap" type="Emp" extends="BaseEmpResultMap">
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
总结
本教程详细介绍了MyBatis的映射关系处理,包括:
- ✅ ResultMap基础使用:字段和属性映射关系处理
- ✅ 多对一映射:三种不同的处理方式
- ✅ 一对多映射:collection和分步查询
- ✅ 延迟加载:配置和使用方法
- ✅ 最佳实践:映射方式选择和性能优化
下一步学习
- 学习MyBatis的动态SQL
- 了解条件查询和批量操作
- 掌握SQL片段的复用
在下一篇文章中,我们将学习MyBatis的动态SQL,包括条件查询、批量操作、SQL片段等高级功能。
</rewritten_file>