JAVASSMResultMap关联映射
定西Endless
所以还是重新学一下这个mybatis这块,sql实在是不会写了…
害,当初没好好学,现在真是追悔莫及
映射关系
表结构:
- t_emp_old:

 
- t_dept:

 
当字段名和属性名不一致时,该怎么办?
- sql中起别名,比如数据库中是emp_id,而Java属性是empId,那么可以这样写sql:
 
1
   | select emp_id empId,emp_name empName ,age,gender from t_emp where emp_id = #{empId};
  | 
 
- 给核心配置文件加上如下设置,就可以自定映射下划线和驼峰
 
1 2 3 4 5
   |     <settings>
          <setting name="mapUnderscoreToCamelCase" value="true"/>
      </settings>
   | 
 
所以当时看这个课的时候,是什么心情呢?
自定义映射resultMap
仔细看看笔记,其实也是挺明白的,就是写的地方优点…
- 多对一的映射关系:
 
- 一对多的映射关系:
 
resultMap最基本的使用
resultMap中的标签/属性
- id设置主键的映射关系
 
- result设置其余字段的映射关系
 
- column字段名
 
- property属性名
 
用于处理数据库表字段和Java属性名不匹配的情况:
依然还是数据库中emp_id,emp_name,而java类是empId和empName
这时就可以使用resultMap来自定义映射关系:
1 2 3 4 5 6 7 8 9 10 11 12
   | <resultMap id="empResultMap" type="Emp">     <id column="emp_id" property="empId"></id>     <result column="emp_name" property="empName"></result>     <result column="age" property="age"></result>     <result column="gender" property="gender"></result> </resultMap>
  <select id="getEmpByEmpId" resultMap="empResultMap">     select *     from t_emp_old     where emp_id = #{empId}; </select>
   | 
 
主要就是emp_id和empId之间的写法,column表示数据库中的字段,而property表示属性名
处理多对一映射关系
场景:查询员工信息以及员工所对应的部门信息
这时,就要注意多对一这种关系如何处理了-如何设置java实体类的属性
- 在一的一方设置多的一方类型的集合
 
- 在多的一方设置一的一方的类的对象
 
Emp.java
1 2 3 4 5 6 7
   | public class Emp {     private Integer empId;     private String empName;     private Integer age;     private String gender;     private Dept dept; }
  | 
 
Dept.java
1 2 3 4 5
   | public class Dept {     private Integer deptId;     private String deptName;     private List<Emp> emps; }
  | 
 
定义根据员工id查询员工和该员工对应的部门信息的方法:
1
   | Emp getEmpAndDeptByEmpIdNew(@Param("empId") Integer empId);
  | 
 
这个就涉及到两个表关联之间的关系了,什么A∪B和A∩B,还有各种各样的关系,害,突然意识到,我可能需要把之前mysql的关联查询也重新学一遍了,这几天就少打点游戏吧,把什么left join,和right join,和inner join都重新看一遍…
mapper映射文件中该怎么写(resultMap和sql)
什么意思呢,就是你看这个方法,是根据员工id查询员工信息已经员工对应的部门信息,而员工类中是有一个部门类的,这时通过sql查询,查询出的dept_id和dept_name要映射为一个Dept对象,此时就要使用association来实现了
一共三种方式来处理字段和类对应的关系:
- 级联
 
- association
 
- 分布查询
 
级联处理
什么意思呢,就是单纯使用resultMap:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   |      <resultMap id="empAndDeptMapJiLian" type="Emp">         <id column="emp_id" property="empId"></id>         <result column="emp_name" property="empName"></result>         <result column="age" property="age"></result>         <result column="gender" property="gender"></result>         <result column="dept_id" property="dept.deptId"></result>         <result column="dept_name" property="dept.deptName"></result>     </resultMap>
           <select id="getEmpAndDeptByEmpIdNew" resultMap="empAndDeptMapJiLian">         SELECT t_emp_old.*,                t_dept.*         FROM t_emp_old                  LEFT JOIN t_dept ON t_emp_old.dept_id = t_dept.dept_id         WHERE t_emp_old.emp_id = 1     </select>
 
  | 
 
区别在哪?
区别在于处理dept_id和dept_name和dept属性的映射关系时,使用了result,column依然是查询出的列名,而property对应的就是dept.deptId和dept.deptName了,这就相当于,将查询出的字段dept_id与Emp类中dept属性中的deptId属性进行对应(有点拗口,但是这么说没问题啊没问题),此时得到正确的数据:

不用在意dept后面有个emps,那个是toString的输出,只要查到deptId和deptName就算成功了
association处理
这里使用association标签进行多对一的映射关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   |      <resultMap id="empAndDeptMap" type="Emp">         <id column="emp_id" property="empId"></id>         <result column="emp_name" property="empName"></result>         <result column="age" property="age"></result>         <result column="gender" property="gender"></result>         <association property="dept" javaType="Dept">             <id column="dept_id" property="deptId"></id>             <result column="dept_name" property="deptName"></result>         </association>     </resultMap>
           <select id="getEmpAndDeptByEmpIdNew" resultMap="empAndDeptMap">         SELECT t_emp_old.*,                t_dept.*         FROM t_emp_old                  LEFT JOIN t_dept ON t_emp_old.dept_id = t_dept.dept_id         WHERE t_emp_old.emp_id = 1     </select>
 
  | 
 
这里要注意什么?
要注意association标签的使用,association标签中的属性有property和javaType等,property是属性名-就是Emp类中Dept属性的属性名dept,而javaType就表示该属性是什么类型的:Dept呗,此时就可以在association中正常的写映射关系了
一对一和多对一有什么区别?
没有区别,处理起来是一样的步骤
还有就是,说白了,association就是用来处理实体类类型的属性的,你看,这个Emp类中有一个Dept类型的属性,就可以通过association来处理,所以它还可以用来处理一对一的关系
分步查询处理:
23点56分
这个明天再看吧,睡觉
分步查询,也就是使用多个sql进行查询,重点在于,这个查询应该分为几步,每一步应该干什么
所以,查询员工信息以及员工对应的部门信息,可以分为两步:
- 根据员工id查询员工信息
 
- 根据员工对应的部门id在部门表中查询部门信息
 
第一步的内容:
接口:
1 2 3 4 5 6
   | 
 
 
 
  Emp getEmpAndDeptByStepOneNew(@Param("empId") Integer empId);
 
  | 
 
sql:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | <resultMap id="empAndDeptOne" type="Emp">     <id column="emp_id" property="empId"></id>     <result column="emp_name" property="empName"></result>     <result column="age" property="age"></result>     <result column="gender" property="gender"></result>     <association property="dept"                  select="com.zzmr.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwoNew"                  column="dept_id">     </association> </resultMap>
 
  <select id="getEmpAndDeptByStepOneNew" resultMap="empAndDeptOne">     select *     from t_emp_old     where emp_id = #{empId}; </select>
   | 
 
这里就要注意association的使用了,比之前多了一个select属性,它适用于指定下一步查询的sql唯一标识,或者说是方法的全路径,而方法的全路径可以使用idea的copy reference,但是注意,拷过来的是长这样:com.zzmr.mybatis.mapper.EmpMapper#getEmpAndDeptByStepOneNew,没错,前面都没问题,但是最后那个方法的位置是变成了#号,所以会导致mybatis找不到对应的sql,要把井号改为点才行,**还有就是column表示给第二步查询传入的参数
下面看第二步查询:
这里将第二步查询的接口放在了DeptMapper中了,当然,放在EmpMapper是一样的,但是为了结构更加清晰明了,还是放在了DeptMapper中
接口
1 2 3 4 5 6
   | 
 
 
 
  Dept getEmpAndDeptByStepTwoNew(@Param("deptId") Integer deptId);
 
  | 
 
sql:
1 2 3 4 5 6
   |  <select id="getEmpAndDeptByStepTwoNew" resultType="Dept">     select *     from t_dept     where dept_id = #{deptId} </select>
 
  | 
 
第二步就简单的多了,可以看出,stepTwo方法接收一个deptId,此时第一步中association中的column就派上用场了,它就用来指定传给第二步查询的参数,这里要根据查询出员工信息中对应的部门id,在部门表中根据id来查询部门信息
association中的property依然是用于将查询的结果赋值给emp中的dept属性(这里由于第二步查询的返回值是一个dept对象,所以第一步不需要再处理一次dept_id和dept_name跟deptId和deptName的映射关系了)
查询结果:

那能用一条sql查出的结果,为什么要用两条sql呢?
这里就要引入分步查询的优点-延迟加载
延迟加载
需要在配置文件中设置全局配置信息
1 2 3 4
   |     <settings>
          <setting name="lazyLoadingEnabled" value="true"/>     </settings>
   | 
 
这样就开启了延迟加载,此时修改测试方法,再执行刚才的byStepOne:
1 2 3 4 5 6 7
   | @Test public void testGetEmpAndDeptByStepNew() {     SqlSession sqlSession = SqlSessionUtil.getSqlSession();     EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);     Emp emp = mapper.getEmpAndDeptByStepOneNew(3);     System.out.println(emp.getEmpName()); }
   | 
 
此时我们只获取emp.getEmpName()而不获取部门信息,此时只会执行一条sql:

把延迟加载配置给注掉,再测试,两条sql:

此时就能感受到延迟加载的好处了
但是延迟加载和另外一个属性也有关:aggressiveLazyLoading,当开启时,任何该对象的方法调用都会加载该对象的所有属性,否则,每个属性都会按需加载
1
   | <setting name="aggressiveLazyLoading" value="false"/>
   | 
 
此时就实现了按需加载,获取的数据是什么,就只会执行相应的sql,此时可以通过association和collection中的fetchType属性设置当前的分布查询是否使用延迟加载,fetchType="lazy"就是开启延迟加载,而等于eager就是立即加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | <resultMap id="empAndDeptOne" type="Emp">     <id column="emp_id" property="empId"></id>     <result column="emp_name" property="empName"></result>     <result column="age" property="age"></result>     <result column="gender" property="gender"></result>     <association property="dept" fetchType="eager"                  select="com.zzmr.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwoNew"                  column="dept_id">     </association> </resultMap>
 
  <select id="getEmpAndDeptByStepOneNew" resultMap="empAndDeptOne">     select *     from t_emp_old     where emp_id = #{empId}; </select>
   | 
 
看,此时fetchType为eager,即使开启了延迟加载和关闭了按需加载,依然是执行全部的sql
总结:
- 想要实现延迟加载,一个
lazyLoadingEnabled=true即可完成,但是老师建议是加上aggressiveLazyLoading=false,这样依然是默认延迟加载 
- 当配置文件如上面所示,又想要实现立即加载,只需要在associaiton中设置
fetchType=eager,即可实现立即加载,而不设置或者是设置fetchType=lazy时,都是延迟加载 
ok,多对一搞定了,应该是搞定了,下面继续看一对多
处理一对多映射关系
一共两种方式:
- collection
 
- 分步查询
 
collection处理一对多
把上面的部门类再拿下来看一下:
Dept.java
1 2 3 4 5
   | public class Dept {     private Integer deptId;     private String deptName;     private List<Emp> emps; }
  | 
 
没错,就是在一的一方设置多的一方的集合,其实就是一句话:对一,对应对象,对多,对应集合
重点就在于,将联查得到的员工信息,封装为一个List集合,下图就是sql查询出的结果,可以看出,部门信息肯定是一样的,不同的地方就在于emp的信息,就要把这多个emp信息封装为一个List<Emp>集合

看sql:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | <resultMap id="deptAndEmpMapNew" type="Dept">     <id column="dept_id" property="deptId"></id>     <result column="dept_name" property="deptName"></result>     <collection property="emps" ofType="Emp">         <id column="emp_id" property="empId"></id>         <result column="emp_name" property="empName"></result>         <result column="age" property="age"></result>         <result column="gender" property="gender"></result>     </collection> </resultMap>
 
  <select id="getDeptAndEmpByDeptIdNew" resultMap="deptAndEmpMapNew">     SELECT *     FROM t_dept              INNER JOIN t_emp_old ON t_emp_old.dept_id = t_dept.dept_id     where t_dept.dept_id = #{deptId} </select>
   | 
 
这里就用到collection标签了,它可以用于处理一对多和多对多的关系,collection的属性也是有property,表示tpye类中的属性名,什么意思呢,往上看Dept类,是不是有一个List<Emp> emps,这个emps就是要填在property中的内容,而ofType就表示要封装的集合的泛型,collection会将结果集中的多条emp信息封装为一个emp集合,而每个emp对象中字段的对应关系,就还是和之前的写法一样.
测试:
1 2 3 4 5 6 7
   | @Test public void testDeptAndEmpByDeptIdNew() {     SqlSession sqlSession = SqlSessionUtil.getSqlSession();     DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);     Dept deptInfo = mapper.getDeptAndEmpByDeptIdNew(2);     System.out.println(deptInfo); }
   | 
 
结果:

分步查询处理一对多
还是分两步,我想想
- 根据部门id查询部门信息
 
- 根据部门id再去员工信息表中查询所有匹配的员工
 
第一步的接口:
1 2 3 4 5 6
   | 
 
 
 
  Dept getDeptAndEmpStepOneNew(@Param("deptId") Integer deptId);
 
  | 
 
sql:
1 2 3 4 5 6 7 8 9 10 11 12 13
   | <resultMap id="deptAndEmpStepMap" type="Dept">     <id property="deptId" column="dept_id"></id>     <result property="deptName" column="dept_name"></result>     <collection property="emps" select="com.zzmr.mybatis.mapper.EmpMapper.getDeptAndEmpStepTwoNew" column="dept_id">     </collection> </resultMap>
 
  <select id="getDeptAndEmpStepOneNew" resultMap="deptAndEmpStepMap">     select *     from t_dept     where dept_id = #{deptId} </select>
   | 
 
第一步查询就是根据部门id查询部门信息,而resultMap才是重点,这里使用collection时,也是使用了select指定下一步查询的sql唯一标识,以及传递的参数dept_id
第二步的接口:
1 2 3 4 5 6
   | 
 
 
 
  List<Emp> getDeptAndEmpStepTwoNew(@Param("deptId") Integer deptId);
 
  | 
 
第二步的sql:
1 2 3 4
   |  <select id="getDeptAndEmpStepTwoNew" resultType="Emp">     select * from t_emp_old where dept_id = #{deptId} </select>
 
  | 
 
这里就是直接使用的resultType,因为查询出的结果就是一个一个的Emp,第二步返回的结果会交给第一步的collection来处理,使多个Emp对象封装为一个List<Emp>集合,最后再将这个集合赋给emps
再看一下延迟加载的效果:
测试代码:
1 2 3 4 5 6 7
   | @Test public void testDeptAndEmpByDeptIdByStepNew() {     SqlSession sqlSession = SqlSessionUtil.getSqlSession();     DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);     Dept deptInfo = mapper.getDeptAndEmpStepOneNew(2);     System.out.println(deptInfo.getDeptName()); }
   | 
 
测试结果:

当只获取getDeptName,此时就不涉及到员工的信息,所以只会执行第一条sql语句..
ok,现在应该是比之前好一点了