自定义映射ResultMap
数据库准备
两个表的关系:
员工表中的did属性名和部门表中主键名一样。
解决字段名和属性名不一致的问题
在之前的我们的学习中,都是有意的将数据库的字段名和实体类属性名设置成一样一样的,但是我们在开发中却不是的。开发中数据库和java的代码规范是不一致的,比如数据库字段中如果有两个单词,我们通常使用_来隔开(如:emp_name),但是java中我们通常使用驼峰命名法(如:empName),这一节我们就是来解决这个问题的。
属性名和字段名不匹配的时候,我们的能匹配的部分是可以正常映射的,不能匹配的部分就会得到null,而不是整个执行失败。
方法一
给字段取别名:
这个主要就是在写sql语句的时候,我们给字段去一个别名,别名必须和实体类属性完全对应上。
<select id="getAllEmp" resultType="com.atguigu.mybatis.pojo.Emp">
select eid,emp_name as empName,age,sex,email from t_emp;
</select>
方法二:设置全局配置,所有的下划线映射为驼峰
<!-- 在mybatis核心配置文件中设置全局属性值-->
<settings> <!--把下划线映射为驼峰 emp_name映射成empName,em_pname映射成emPname-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<select id="getAllEmp" resultType="com.atguigu.mybatis.pojo.Emp">
select eid,emp_name empName,age,sex,email from t_emp;
select * from t_emp;
</select>
一劳永逸,但是只针对于下划线到驼峰的映射,这要求我们在开发中必须遵循开发规范,如果去用别人的代码,人家没有遵循命名规范,就非常麻烦。
方法三:使用ResultMap进行自定义映射
<!-- resultMap:设置自定义映射关系
id:唯一标识
type:设置映射关系中的实体类类型-->
<resultMap id="empResultMap" type="Emp">
<!-- id设置主键的映射关系,result设置普通字段的映射关系
property设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
column:设置映射关系中的字段名,必须是sql语句查询出的字段名-->
<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" resultMap="empResultMap">
select *
from t_emp;
</select>
这个东西的映射非常像取别名,但是也是做一次映射,永久享受的事情,并且,这个映射还能我们手动修改,非常的方便,完美的解决了方法二的一个缺陷。只是好像代码会麻烦一点,我个人比较推荐。
多对一的映射关系
以部门和员工表为例子,一对多和多对一是一个概念,那就是一个部门有多个员工(每个员工属于某一个部门)但是我们做程序开发时,主要抓住一个概念就行。
比如说两张表emp,dept,emp表中有dept表中的部门编号属性,通过这个属性可以查询对应的信息,我们就可以在emp表所对应的实体类中创建对象dept,来表示当前员工所对应的部门对象,我们知道一个部门有多个员工,我们就可以在部门表对应的实体类中设置一个属性,表示员工的集合。
但是这里有一个问题需要我们来解决,我们在mybatis中,通过字段名和属性进行映射,但是,举个例子:我们想要查询员工表的部门编号和部门表的部门编号相等的员工信息,员工表对应的实体类中属性是Dept dept,但是查询出来的结果中,会有部门名字和部门编号,这个时候就无法进行映射,那要怎么解决?
数据库实现如下:
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = 2
接下来给出具体解决方法:
方法一:级联属性赋值
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = 2
通过级联查询, t_dept on t_emp.did = t_dept.did where t_emp.eid = 2,我们能的到一个Dept的对象且符合后面条件的一条数据,所以我们需要将该对象设置成一个Emp属性,然后进行赋值,我们就能根据emp对象得到符合条件的dept的对象。
/**
* 查询某指定员工对应的信息以及其所在部门信息
*/
Emp getEmpAndDept(@Param("eid") Integer eid);
<!--处理多对一映射关系方式一:级联属性赋值-->
<!--这个id属性我们需要在哪里去调用,我们要知道----resultMap = ""-->
<resultMap id="empAndDeptpResultMapOne" 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>
<!--对对象的映射是关键,我们主要就是需要得到did和deptName
让查询出来的did,dept_name对应emp实体类中的dept里面的相应属性,特别注意,我们要对应实体类dept中的属性,必然我们的dept实体类必不可少
-->
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<!--emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptpResultMapOne">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
测试代码:
@Test
public void getEmpAndDeptOneTest(){
SqlSession sqlSession = getSqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDept(2);
System.out.println(emp);
}
方法二:通过assocation解决多对一问题
<!--处理多对一映射关系方式一:assocation-->
<resultMap id="empAndDeptpResultMapTwo" 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>
<!--对对象的映射是关键,我们主要就是需要得到did和deptName-->
<association property="dept" javaType="Dept">
<id property="deptName" column="dept_name"></id>
<id property="did" column="did"></id>
</association>
</resultMap>
<!--emp getEmpAndDept(@Param("eid") Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptpResultMapTwo">
SELECT *
FROM t_emp
LEFT JOIN t_dept ON t_emp.did=t_dept.did
WHERE t_emp.did=#{eid};
</select>
@Test
public void getEmpAndDeptTwoTest(){
SqlSession sqlSession = getSqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDept(2);
System.out.println(emp);
}
这个方法和级联属性赋值类似,我们主要是assocation标签里面有区别。
方法三:分步查询(用得比较多)
上面两种方法都是通过一条sql语句来查询,如果我们有很多表,做连接的操作非常多,逻辑非常难以理清楚,我们是否可以将一条语句拆开呢?当然是可以的,并且我们在开发中还比较喜欢拆开来查询再连接。
- 第一步:查询员工信息
/**
* 分布查询第一步:
* 根据eid查询指定员工信息
*/
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
- 第二步:查询部门信息
/**
* 分布查询第二步:
* 根据did查询部门信息
*/
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
注意在这里新增了DeptMapper映射,所以我们也需要去核心文件配置哟。
第三步:测试
@Test
public void getEmpAndDeptTwoTest(){
SqlSession sqlSession = getSqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(2);
System.out.println(emp);
}
延迟加载
之前我们在多对一的时候解释了为什么要写多个sql进行分步,那么我们还有其它的理由选择分布查询呢?
实现延迟加载是分布查询的另一个原因。
延迟加载:只加载我们需要的sql语句并执行,上面员工部门例子,我们就是如果只查询员工信息,就可以不用加载查询部门的sql,这样可以节省资源。如果我们要查询员工以及对应的部门,就需要加载查询员工和部门的sql。
在mybatis中,延迟加载默认是不开启延迟加载的,我们如果想要实现延迟加载,就必须在核心配置文件中设置全局配置信息(settings标签)。
<settings>
<!-- 把下划线映射为驼峰 emp_name映射成empName,em_pname映射成emPname-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载-->
<!--延迟加载的属性可以去mybatis的官方文档中去查看-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
lazyLoadingEnabled:延迟加载的全局开关,当开启时,所有关联对象(分步查询的第一步,第二步…)都会延迟加载
aggresslveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每一个属性会按需加载
延迟加载指的是当我们访问哪一些信息,它就会去执行相应的sql,如果没有访问,则没有执行
此时就可以实现按需加载,获取的数据是什么就只会执行相应的sql,此时可以通过assocation和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载) | eager(立即加载)”
一对多映射关系
一对多就是说一个部门有多个员工,我们要找到某部门的所有员工,那么我们在部门的里面需要有一个员工集合的属性 --------- List empList;
方法一:通过collection
<!--获取部门以及部门中所有员工信息-->
<!--Dept getDeptAndEmp(@Param("did") Integer did);-->
<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>
与多对一真的association非常的相似,但是需要注意collection中的属性。
property:表示需要赋值的属性,通常是一个List集合类型的属性
collection:处理一对多的映射关系
ofType:这里我们不设置javaType属性,ofType代表的是List集合储存的实体类类型,而不是List的类型。
方法二:分布查询
这里的分布查询和多对一的分布查询差不多,但是我们还是需要注意一些不同点,如下:
<resultMap id="deptAndEmpByStepResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did">
</collection>
</resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStepResultMap">
select * from t_dept where did = #{did};
</select>
几乎就是无差别,并且也支持延迟加载哟!!!