resultMap
1. 当表的字段与实体类的属性不一致
例如我的表里的字段是emp_name javaBean类里面是name, 这时mybatis自动映射不能帮忙了
1. 将 name 改为 empName 就可以
2. 用resultMap来映射
在mapper.xml文件里面
<resultMap type="org.mybatis.pojo.Employee" id="empMap">
<!-- 用id属性来映射主键字段 -->
<id property="id" column="id"/>
<!-- 用result属性来映射非主键字段 -->
<result property="name" column="emp_name"/>
<result property="age" column="age"/>
<result property="did" column="d_id"/>
</resultMap>
<select id="selectById" resultMap="empMap"> //resultMap 和 和上面的id="empMap"对应
select * from emp where id=#{id}
</select>
注意 insert update delete是没有resultType和resultMap属性的
2. 一对多时 如何使用?
(1)使用级联属性
<resultMap type="org.mybatis.pojo.Employee" id="joinMap">
<!-- 用id属性来映射主键字段 -->
<id column="id" property="id" />
<!-- 用result属性来映射非主键字段 -->
<result column="emp_name" property="empName" />
<result column="age" property="age" />
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.deptName"/>
</resultMap>
<!-- 当我要联表查询 一个部门有多个员工时 public Employee selectWithDept(Integer id); -->
<select id="selectWithDept" resultMap="joinMap">
select e.id id,
e.emp_name emp_name,
e.age age,
e.d_id d_id,
d.id did,
d.dept_name dept_name
FROM emp e, dept d
WHERE
e.d_id = d.id
AND e.id = #{id}
</select>
这里column 与你的SQL语句中要查询的列要对应 ,property是对应的POJO属性对应
我将这里的列名 改掉 ,则会查不出对应的部门信息
(2)可以通过 association来替代级联
<!-- association可以指定联合的javaBean对象
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
-->
<resultMap type="org.mybatis.pojo.Employee" id="myMap">
<!-- 用id属性来映射主键字段 -->
<id column="id" property="id" />
<!-- 用result属性来映射非主键字段 -->
<result column="emp_name" property="empName" />
<result column="age" property="age" />
<association property="dept" javaType="org.mybatis.pojo.Department">
<result column="did" property="id"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
(3)可以通过 association实现分步查询
绑定mapper出现BindingException 要注意<mapper namespace="org.mybatis.mapper.DepartmentMapper">不要写成了POJO的全类名
<!-- 使用association进行分步查询:
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值去部门表查出部门信息
3、部门设置到员工中;
-->
public interface EmployeeMapper {
public Employee getEmpByIdStep(Integer id);
<resultMap type="org.mybatis.pojo.Employee" id="assMap">
<!-- 用id属性来映射主键字段 -->
<id column="id" property="id" />
<!-- 用result属性来映射非主键字段 -->
<result column="emp_name" property="empName" />
<result column="age" property="age" />
<result column="d_id" property="did" />
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept"
select="org.mybatis.mapper.DepartmentMapper.findDeptById"
column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="assMap">
select * from emp where id=#{id}
</select>
Department里面
public interface DepartmentMapper {
public Department findDeptById(Integer id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.mapper.DepartmentMapper">
<select id="findDeptById" resultType="org.mybatis.pojo.Department">
select id , dept_name deptName from dept where id=#{id}
</select>
</mapper>
最后的单元测试
@Test
public void test3() throws Exception{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpByIdStep(2);
System.out.println(emp);
System.out.println(emp.getDept());
} finally {
openSession.close();
}
}
可以看到控制台发了两个SQL ,Employee显示出了全部的内容
(4)延迟加载(懒加载)
association和collection还具备延迟加载的功能
在mybatis-config文件中配置下面的
lazyLoadingEnabled 默认是false) | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading (默认是false, true in ≤3.4.1)) | 当开启时,任何方法的调用都会加载该对象的所有属性 。否则,每个属性会按需加载(参考lazyLoadTriggerMethods). |
我的mybatis版本3.4.1的
aggressiveLazyLoading默认是true,需要改为false
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
将这个<setting name="aggressiveLazyLoading" value="false"/>注释掉 只开启一个 依然是发送的两个SQL
开启延时加载最好是同时设置这两个,
@Test
public void test3() throws Exception{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpByIdStep(2);
System.out.println(emp.getEmpName());
} finally {
openSession.close();
}
}
没开启就会发送2个SQL
开启后只发送了一条SQL
(5) resultMap高级映射collection的使用
当我要查询 一个部门的所有员工时 ,比如需要查出开发部里的全部员工 , 返回的是查询的部门中包含多名员工
这时将关联的员工信息封装起来 需要用collection来处理
DepartmentMapper.xml里面设置resultMap
<!--嵌套结果集的方式 使用collection标签定义关联的集合类型的属性封装规则 -->
<resultMap type="org.mybatis.pojo.Department" id="myCollection">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
<!--collection里面定义关联集合类型属性的封装
odType:指定集合里面元素的类型
-->
<collection property="emps" ofType="org.mybatis.pojo.Employee">
<id column="eid" property="id"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<select id="getDeptByIdPlus" resultMap="myCollection">
select d.id did,dept_name dept_name , e.id eid, e.emp_name emp_name,e.age age
from dept d
LEFT JOIN emp e
on d.id=e.d_id
WHERE d.id=#{id}
</select>
DepartmentMapper.java 接口中定义这个方法
public Department getDeptByIdPlus(Integer id);
这里的collection用于封装查询到的Employee信息
<!--collection里面定义关联集合类型属性的封装
odType:指定集合里面元素的类型
-->
这里要先在Department POJO类 加上一个集合属性
public class Department {
private Integer id;
private String deptName;
private List<Employee> emps;//加上这个集合属性
这样就可以查到 一个部门 下的 所有员工
@2 与association类似,collection同样支持分段查询 以及延迟加载
分段查询: 同样地 要查询一个部门下的所有员工,可以分阶段 先查部门 再查询员工
1.首先在两个mapper加上接口方法
public Department getDeptByIdStep(Integer id); ===》》 DepartmentMapper
public List<Employee> getEmpsByIdStep(Integer deptId); ===》》 EmployeeMapper
2.对应的Departmentmapper.xml 以及 EmployeeMapper.xml
<!--collection分段查询 -->
<resultMap type="org.mybatis.pojo.Department" id="stepCollection">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emps"
select="org.mybatis.mapper.EmployeeMapper.getEmpsByIdStep"
column="{deptId=id}">
</collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="stepCollection">
select id,dept_name from dept where id=#{id}
</select>
<select id="getEmpsByIdStep" resultType="org.mybatis.pojo.Employee">
select * from emp where d_id=#{deptId}
</select>
通过测试,可以看到发送了两个SQL
对于延迟加载 是按需加载,若只要查询部门名,就只会发一条SQL
<!-- 扩展:多列的值传递过去:
将多列的值封装map传递;
column="{key1=column1,key2=column2}"
fetchType="lazy":表示使用延迟加载;
- lazy:延迟 - eager:立即 //即使全局配置了延迟加载 也可以在这里让其立即加载
@3 discriminator鉴别器 (用的比较少 了解下)
<!-- =======================鉴别器============================ -->
<!-- <discriminator javaType=""></discriminator>
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装Employee:
如果查出的是女生:就把部门信息查询出来,否则不查询;
如果是男生,把last_name这一列的值赋值给email;
<resultMap type="org.mybatis.pojo.Employee" id="DisMap">
<!-- 用id属性来映射主键字段 -->
<id column="id" property="id" />
<!-- 用result属性来映射非主键字段 -->
<result column="emp_name" property="empName" />
<result column="age" property="age" />
<discriminator javaType="int" column="age">
<case value="14" resultType="org.mybatis.pojo.Employee">
<association property="dept"
select="org.mybatis.mapper.DepartmentMapper.findDeptById"
column="d_id">
</association>
</case>
<case value="24" resultType="org.mybatis.pojo.Employee">
<id column="id" property="id"/>
<result column="emp_name" property="empName"/>
<result column="emp_name" property="age"/>
</case>
</discriminator>
</resultMap>