6_自定义映射resultMap
1.resultMap
处理字段和属性的映射关系
方式一:字段名与属性名不一致,用别名
解决
select * from emp;
select eid,emp_name as empName,age,sex,email from emp;别名方式
方式二:核心配置文件中setters
标签配
在setters
标签配置mapUnderscoreToCamelCase
属性,值设置为true
是开户驼峰命名转换,默认是false
是不开启驼峰命名转换
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | `true | false` |
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
方式三:自定义映射关系
resultMap
自定义标签相关的属性
父级标签
resultMap
:设置自定义映射关系id
:唯一标识,不能重复type
:设置映射关系中的实体类类型【就是查询结果给那个JavaBean
赋值】
子标签:【重点理解】
-
id
:设置主键的映射关系 -
result
:设置普通字段映射关系属性:
property
:设置映射关系中的属性名,必须是type
属性所设置的实体类类型中的属性名【就是type
设置JavaBean
类中的属性】column
:设置映射关系中字段名,必须是sql
语句查询出字段名【就是查询语句中的字段】
案例:
<!--
查询所有员工,并且使用resultMap解决字段名与属性名不一致问题
-->
<!--
resultMap:设置自定义映射关系
id:唯一标识,不能重复
type:设置映射关系中的实体类类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
属性:
property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
column:设置映射关系中的字段名,必须是sql语句查询出的字段名
-->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
</resultMap>
<select id="getAllEmp" resultMap="empResultMap">
select * from emp;
</select>
综合:
Mapper
接口
/**
* 查询所有的员工信息
*/
List<Emp> getAllEmp();
Mapper.xml
文件
<!--
查询所有员工,并且使用resultMap解决字段名与属性名不一致问题
-->
<!--
resultMap:设置自定义映射关系
id:唯一标识,不能重复
type:设置映射关系中的实体类类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
属性:
property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
column:设置映射关系中的字段名,必须是sql语句查询出的字段名
-->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
</resultMap>
<select id="getAllEmp" resultMap="empResultMap">
select * from emp;
</select>
<!--List<Emp> getAllEmp();查询所有员工信息-->
<select id="getAllEmpOld" resultType="Emp">
<!--
由于:`select * from emp;`
方式字段名`emp_name`与属性`empName`名不一致,
所以`empName`没有被赋值,返回null值,
解决方案一:使用别名,为`empName`起与属性一致的别名
解决方案二:使用全局配置:<setting name="mapUnderscoreToCamelCase" value="true"/>:开启驼峰命名转换
-->
<!--
select * from emp;
如果需要使用这种方式可以在核心配置文件中,在`setter`标签中
配置:mapUnderscoreToCamelCase属性,值为ture,默认是false,不支持驼峰命名转换
<setting name="mapUnderscoreToCamelCase" value="true"/>
-->
select eid,emp_name as empName,age,sex,email from emp;<!--别名方式-->
select * from emp;
</select>
test
类
// 查询所有员工信息
@Test
public void testGetAllEmp(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> allEmp = mapper.getAllEmp();
// select * from emp;此方式查询,由于字段名与属性名不一致,
// 解决方案是:为字段名起别名,为`empName`起与属性一致的别名
// 如:select eid,emp_name as empName,age,sex,email from emp;
allEmp.forEach(emp -> System.out.println(emp));
}
2.多对一映射处理
多对一映射关系,必须要在多的方添加一的对象属性
**案例:**员工对应部门信息,就是典型的多对一映射关系,需要在员工
类中添加部门
的对象属性【并且添加getter
方法和setter
方法】
// 处理多对一映射关系,必须要多的一方添加一的方的对象属性
private Dept dept;
并添加:setter方法和getter方法
方式一:级联属性赋值
Mapper
接口
/**
* 根据ID查询员工信息及对应部门信息
*/
Emp getEmpAndDeptById(@Param("eid") Integer eid);
Mapper.xml
文件
<!--处理多对一映射关系方式一:级联属性赋值-->
<resultMap id="empAndDeptResultMap" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
<result property="dept.did" column="did"/>
<result property="dept.deptName" column="dept_name"/>
</resultMap>
<!--getEmpAndDeptById(@Param("eid") Integer eid);根据ID查询员工信息及对应部门信息-->
<select id="getEmpAndDeptById" resultMap="empAndDeptResultMap">
select e.eid,e.emp_name,e.age,e.sex,e.email,d.did,d.dept_name
from emp as e inner join dept as d on e.did=d.did
where e.eid=#{eid};
</select>
test
类
// 查询指定ID员工的信息及对应部门信息
@Test
public void testGetEmpAndDeptById(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptById(1);
System.out.println(emp);
//使用级联属性赋值:
// Emp{eid=1, empName='明天', age=21, sex='女', email='123@qq.com', dept=Dept{did=1, deptName='A'}}
}
方式二:使用association
处理映射关系【重点】
例如:
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name" ></result>
</association>
标签解释:
association
父级标签:
property
: 设置是resultMap
中type
属性值中的javabean
类中dept
赋值javaType
表示是另一个Javabean
类
子标签:
id
表示为javaType
属性中的Dept
的属性id
值赋值
property
表示为javaType
属性中的Dept
的属性赋值
意思是:先设置javaTyp
属性是将查询到的字段值为javaType
的javabean
对象赋值,赋值完成后再将javaType
赋值的对象给propery
的属性赋值
Mapper.xml
文件
<!--处理多对一映射关系方式二:association属性解决-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
<association property="dept" javaType="Dept">
<id property="did" column="did"/>
<result property="deptName" column="dept_name"/>
</association>
</resultMap>
<!--getEmpAndDeptById(@Param("eid") Integer eid);根据ID查询员工信息及对应部门信息-->
<select id="getEmpAndDeptById" resultMap="empAndDeptResultMapTwo">
select e.eid,e.emp_name,e.age,e.sex,e.email,d.did,d.dept_name
from emp as e inner join dept as d on e.did=d.did
where e.eid=#{eid};
</select>
方式三:分步查询【最重要推荐使用】
分步查询就是将一条sql
语句,分成多步查询获取最终结果
同样需要使用association
标签,标签需要这两个属性select
和column
标签属性说明:
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
步骤的解释:
- 先是第一步查询到的
did
【部门id
】给第二步查询的条件赋值,使用的属性是column
- 第二步查询到的结果给
Dept
对象赋值 - 第三步是将第二的查询到的
Dept
对象值赋值给指定的属性 如:<association property="dept"
注意是:select
是指第二条sql
的位置【位置是mapper接口的全类名.方法名
】
例如:
第一步:
<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>
<!--
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
fetchType="lazy|eager":lazy表示延迟加载[默认值],eager表示立即加载
-->
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="eager"></association>
</resultMap>
第二步:【查询到的结果赋值给Dept对象】
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from dept where did = #{did};
</select>
案例:
Mapper
接口
第一步
/**
* 分步查询员工信息及对应的部门信息
* 由于每一员工只能对应一个部门,所以需要查询员工信息,
* 再根据员工对应的部门ID,查询部门信息
*/
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
第二步
// 分步查询第二步:根据部门ID查询部门信息
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
Mapper.xml
文件
第一步:
<!--处理多对一映射关系方式二:association属性解决-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
<association property="dept" javaType="Dept">
<id property="did" column="did"/>
<result property="deptName" column="dept_name"/>
</association>
</resultMap>
<!--
getEmpAndDeptByStepOne(@Param("eid") Integer eid);
分步查询员工及部门信息
-->
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepOneResultMap">
select * from emp where eid = #{eid};
</select>
第二步:
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from dept where did = #{did};
</select>
test
类
// 分步查询员工及对应部门信息
@Test
public void testGetEmpAndDeptByStep() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp);
}
延迟加载【重点】
延迟加载的好处是:我们可以单独调用查询语句的每一步,实现查询之间的独立性【同时也是分步查询的好处】
开启延迟加载需要在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载【当我们在全局配置文件中开启加载,在`Mapper.xml`所有的分步查询都是开启的】,lazyLoadingEnabled:设置为true为开启
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载【默认是不开启时[false]】,当开启时我们调用对象中的任何一个属性都会加载所有属性,所以我们需要不开启该属性,达到延迟加载功能[aggressiveLazyLoading:false代表关闭,也是默认值]
开启延迟加载还要与Mapper.xml
文件中的fetchType
属性搭配使用,一般该属性设置为lazy
【也是默认值】,如:fetchType="lazy|eager":lazy
表示延迟加载【也是默认值,一般设置该值】,eager
表示立即加载
<!--
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
fetchType="lazy|eager":lazy表示延迟加载【也是默认值,一般设置该值】,eager表示立即加载
-->
<association property="dept"
select="com.haikang.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy"
/>
案例:
全局配置文件中
<settings>
<!--开启驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载,传入true-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--aggressiveLazyLoading:false,表示加载某个属性,不会倒致所有属性加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
Mapper.xml
文件
<resultMap id="getEmpAndDeptByStepOneResultMap" type="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
<!--
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
fetchType="lazy|eager":lazy表示延迟加载【也是默认值,一般设置该值】,eager表示立即加载
-->
<association property="dept"
select="com.haikang.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy"
/>
</resultMap>
3.一对多映射处理
一对多的关系,例如一个部门有多名员工,一个班级有多名学生
解决方案是:在一的方
添加集合并且指定集合的泛型是多方类型
【就是JavaBean
类型】
// 在一的方添加集合,泛型指定为多的一方类型,并且提供getter和setter方法
private List<Emp> emps;
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
方式一:collection
方式
collection
标签的说明:
ofType
是为集合指定类型
property
是为哪个属性赋值
例如
<!--
ofType:设置collection标签所处理的集合属性中存储数据的类型
-->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="ename" column="ename"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</collection>
案例:
Mapper
接口
/**
*根据部门ID查询该部门中所有员工
*/
Dept getDeptAndEmpByID(@Param("did")Integer did);
Mapper.xml
文件
<resultMap id="DeptAndEmpResultMap" type="Dept">
<id property="did" column="did"/>
<result property="deptName" column="dept_name"/>
<!--
collection:表示为集合赋值,property表示为Dept类中的emps赋值
ofType:表示集合的类型是Emp
-->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
</collection>
</resultMap>
<!--
根据部门ID查询对应的部门所有员工
getDeptAndEmpByID(@Param("did")Integer did);
-->
<select id="getDeptAndEmpByID" resultMap="DeptAndEmpResultMap">
select d.did,d.dept_name,e.eid,e.emp_name,e.age,e.sex,e.email
from dept as d inner join emp as e on d.did=e.did where d.did = #{did};
</select>
test
类
// 一对多使用collection解决
@Test
public void testGetDeptAndEmpByID(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.getDeptAndEmpByID(1);
System.out.println(dept);
List<Emp> emps = dept.getEmps();
emps.forEach(emp -> System.out.println(emp));
}
方式二:分步查询【重点】
分步查询就是将一条sql
语句,分成多步查询获取最终结果
同样需要使用association
标签,标签需要这两个属性select
和column
标签属性说明:
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
案例:查询指定部门信息及该部门的所有员工
步骤的解释:
- 先是第一步查询到的
did
【部门id
】给第二步查询的条件赋值,使用的属性是column
- 第二步查询到的结果给
Emp
对象赋值 - 第三步是将第二的查询到的
Dept
对象值赋值给指定的属性 如:<association property="emps"
注意是:select
是指第二条sql
的位置【位置是mapper接口的全类名.方法名
】
案例:查询指定部门信息及该部门的所有员工
Mapper
接口
第一步:
/**
* 分步查询第一步;
* 根据部门ID查询部门信息及该部门所有员工
* 先查询出该部门的ID
* 再根据该部门ID查询该的所有员工
*/
Dept getDeptAndEmpByStepOnt(@Param("did")Integer did);
第二步:
/**
* 分步查询第二步:
* 根据部门ID查询该部门对应所有员工
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);
Mapper.xml
文件
<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"/>
<result property="deptName" column="dept_Name"/>
<!--
select:设置分步查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
fetchType="lazy|eager":lazy表示延迟加载【也是默认值,一般设置该值】,eager表示立即加载
-->
<collection property="emps"
select="com.haikang.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"
fetchType="lazy"/>
</resultMap>
<!--
getDeptAndEmpByStepOnt(@Param("did")Integer did);
分步查询:第一步先查询出该部门的ID
第二步再根据该部门ID查询该部门的所有员工
-->
<select id="getDeptAndEmpByStepOnt" resultMap="DeptAndEmpByStepOneResultMap">
select * from dept where did = #{did};
</select>
test
类
// 一对多使用分步查询
@Test
public void testGetDeptAndEmpByStep(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.getDeptAndEmpByStepOnt(1);
System.out.println(dept);
List<Emp> emps = dept.getEmps();
emps.forEach(emp -> System.out.println(emp));
}
总结
如果查询只是单表时,字段名与属性名不一致,使用别名
解决
如果查询是多表时,使用分步查询,实现查询之间的独立性【同时也是分步查询的好处】