Mybatis 学习之路其四:级联

一、前言

我参加工作的初期,对于mybatis还不太了解,开发的过程更多是参考公司大牛的代码,然后依样画葫芦。当时设计到 A 类里包含 B 类,然后查询 A 类相关信息(包含B类)的时候,我当时还不懂级联,使用的是一个笨方法,重新建了一个类,这个类包含了 A 类和 B 类的所有信息。这是一个非常笨的方法。事实上,利用 mybatis 的级联功能,能够很简单的查询一对一,和一对多的关系。

二、一对一关系查询

 在我们使用的 mysql 的测试数据中个,我们选出一个一对一的关系进行测试,比如:员工表,和员工-部门号表。前者是员工的基本信息,我们前面使用了很多次,后面是员工号和部门号的对应关系。现在,我们要在获取员工基本信息的时候,把员工的部门号一并获取过来。

首先,修改下员工的信息的 javabean:

 1 import java.util.Date;
 2 import java.util.List;
 3 import salary.model.SalaryModel;
 4 public class EmployeeModel {
 5     private int empNo;
 6     private Date birthDate;
 7     private String firstName;
 8     private String lastName;
 9     private String gender;
10     private Date hireDate;
11     private String deptNo;
12     private List<SalaryModel> salaries = null;
13     public int getEmpNo() {
14         return empNo;
15     }
16     public void setEmpNo(int empNo) {
17         this.empNo = empNo;
18     }
19     public Date getBirth_date() {
20         return birthDate;
21     }
22     public void setBirth_date(Date birth_date) {
23         this.birthDate = birth_date;
24     }
25     public String getFirstName() {
26         return firstName;
27     }
28     public void setFirstName(String firstName) {
29         this.firstName = firstName;
30     }
31     public String getLastName() {
32         return lastName;
33     }
34     public void setLastName(String lastName) {
35         this.lastName = lastName;
36     }
37     public String getGender() {
38         return gender;
39     }
40     public void setGender(String gender) {
41         this.gender = gender;
42     }
43     public Date getHireDate() {
44         return hireDate;
45     }
46     public void setHireDate(Date hireDate) {
47         this.hireDate = hireDate;
48     }
49     
50     public String toString() {
51         StringBuilder strBuilder = new StringBuilder();
52         strBuilder.append("empNo : ");
53         strBuilder.append(empNo);
54         strBuilder.append(" firstName : ");
55         strBuilder.append(firstName);
56         strBuilder.append(" lastName : ");
57         strBuilder.append(lastName);
58         strBuilder.append(" deptNo : ");
59         strBuilder.append(deptNo);
60         strBuilder.append("\nsalaries:\n");
61         if(salaries != null) {
62             for(SalaryModel index : salaries) {
63                 strBuilder.append(index.toString());
64                 strBuilder.append("\n");
65             }
66         }
67         return strBuilder.toString();
68     }
69     public List<SalaryModel> getSalaries() {
70         return salaries;
71     }
72     public void setSalaries(List<SalaryModel> salaries) {
73         this.salaries = salaries;
74     }
75     public String getDeptNo() {
76         return deptNo;
77     }
78     public void setDeptNo(String deptNo) {
79         this.deptNo = deptNo;
80     }
81 }

为了后面测试一对多关系,我提前在 bean 里加了存储工资信息的 List。

然后针对员工-部门号表,我们需要建立对应的 bean,dao,mapper:

bean:

 1 import java.util.Date;
 2 
 3 public class DeptManager {
 4     private int empNo;
 5     private String deptNo;
 6     private Date fromDate;
 7     private Date toDate;
 8     public int getEmpNo() {
 9         return empNo;
10     }
11     public void setEmpNo(int empNo) {
12         this.empNo = empNo;
13     }
14     public String getDeptNo() {
15         return deptNo;
16     }
17     public void setDeptNo(String deptNo) {
18         this.deptNo = deptNo;
19     }
20     public Date getFromDate() {
21         return fromDate;
22     }
23     public void setFromDate(Date fromDate) {
24         this.fromDate = fromDate;
25     }
26     public Date getToDate() {
27         return toDate;
28     }
29     public void setToDate(Date toDate) {
30         this.toDate = toDate;
31     }
32     
33 }

dao:

public interface DeptManagerDao {
    String getDeptNoByEmpNo(int empNo);
}

mapper:

<?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 = "employee.dao.DeptManagerDao">
   <resultMap type="employee.model.DeptManager" id="deptManager">
     <result column = "emp_no" property = "empNo"/>
     <result column = "dept_no" property = "deptNo"/>
     <result column = "from_date" property = "fromDate"/>
     <result column = "to_date" property = "toDate"/>  
   </resultMap>
   <select id = "getDeptNoByEmpNo" parameterType = "int" resultType = "java.lang.String">
     select dept_no from dept_manager where emp_no = #{empNo}
   </select>
 </mapper>
 

然后记得在 mybatis-config 里加载这个新建的 mapper:

<mapper resource="employee/mapper/DeptManagerMapper.xml"/>

到目前为止,没有任何特殊的地方,也没有关于一对一的任何操作,接下来是重点,首先,我们再 employee 的 resultMap 里加入我们新建的 dempNo,但是加入的方式和其他不同:

 <association property = "deptNo" column = "emp_no" select = "employee.dao.DeptManagerDao.getDeptNoByEmpNo"/>

首先,它的元素不在是 result,而是 association,association 这里我们用到了 3 个属性:

property 和 result 元素一样,对应了 javaBean 里的属性;

column 对应了数据库里的列,注意,这个列不是去和 column 对应的,而是作为参数去传给其他 sql 的,传给谁?;

select 决定了参数的传递对象。

我们先看,这样配置之后,我们再获取 employee 信息的时候发生了什么。

运行测试代码:

1 SqlSession sqlSession = SqlSessionFactoryUtils.openSqlSession();         
2     EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
3     EmployeeModel model = employeeDao.getEmployeeByEmpNo(111784);
4     System.out.print(model);

然后 log4j 的输出是:

[DEBUG] 2018-10-01 17:17:44,817: [Thread-main : org.apache.ibatis.datasource.pooled.PooledDataSource]  Created connection 936906727.
[DEBUG] 2018-10-01 17:17:44,817: [Thread-main : org.apache.ibatis.transaction.jdbc.JdbcTransaction]  Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37d80fe7]
[DEBUG] 2018-10-01 17:17:44,821: [Thread-main : employee.dao.EmployeeDao.getEmployeeByEmpNo]  ==>  Preparing: select emp_no,first_name,birth_date,last_name,gender,hire_date from employees where emp_no = ? 
[DEBUG] 2018-10-01 17:17:44,865: [Thread-main : employee.dao.EmployeeDao.getEmployeeByEmpNo]  ==> Parameters: 111784(Integer)
[DEBUG] 2018-10-01 17:17:44,911: [Thread-main : employee.dao.DeptManagerDao.getDeptNoByEmpNo]  ====>  Preparing: select dept_no from dept_manager where emp_no = ? 
[DEBUG] 2018-10-01 17:17:44,911: [Thread-main : employee.dao.DeptManagerDao.getDeptNoByEmpNo]  ====> Parameters: 111784(Integer)
[DEBUG] 2018-10-01 17:17:44,914: [Thread-main : employee.dao.DeptManagerDao.getDeptNoByEmpNo]  <====      Total: 1

我们发现,在执行了 employee.mapper 里的根据 emp_no 查询员工信息之后,mybatis 又自动执行了一条 sql,这条 sql 就是我们再 select 属性里指定的方法,而这个方法是有一个参数的,系统吧 column 指定的列的值传递了过去,然后将这条 sql 获取的值,装填到 employee 的 deptNo 属性中去。

System.out.println 的结果是:empNo : 111784 firstName : Marjo lastName : Giarratana deptNo : d009 (截取了关键部分)。

可以猜出,这条 sql 的发起点是在 resultmap 里。

光是这样,还不足以体现 mybatis 级联的强大,毕竟上述的例子,只要一个 left join 就能搞定,无需这么麻烦。因为,我们需要从其他表获取的不过一个字段而已,但是如果我们获取的是一个对象,就非常麻烦了,又或者是一个对象的集合。下面的一对多的级联就是一个对象的集合。

三、一对多关联查询

这次我们用工资表和员工表关联,每个月都会发工资,所以员工表和工资表必定能是一对多关联。 

还是先把 工资表相关的准备工作做好

bean:

 1 import java.util.Date;
 2 
 3 public class SalaryModel {
 4     private int empNo;
 5     private int salary;
 6     private Date fromDate;
 7     private Date toDate;
 8     
 9     public int getEmpNo() {
10         return empNo;
11     }
12 
13     public void setEmpNo(int empNo) {
14         this.empNo = empNo;
15     }
16 
17     public int getSalary() {
18         return salary;
19     }
20 
21     public void setSalary(int salary) {
22         this.salary = salary;
23     }
24 
25     public Date getFromDate() {
26         return fromDate;
27     }
28 
29     public void setFromDate(Date fromDate) {
30         this.fromDate = fromDate;
31     }
32 
33     public Date getToDate() {
34         return toDate;
35     }
36 
37     public void setToDate(Date toDate) {
38         this.toDate = toDate;
39     }
40 
41     public String toString() {
42         return "empNo: " + empNo + " salary: " + salary + " from : " + fromDate + " to : " + toDate + "\n";
43     }
44 }

dao:

import java.util.List;

import salary.model.SalaryModel;

public interface SalaryDao {
    List<SalaryModel> getSalaryByEmpNo(int empNo);
}

mapper:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3   PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <mapper namespace="salary.dao.SalaryDao">
 6   <resultMap type = "salary.model.SalaryModel" id = "salary">
 7   <result column = "emp_no" property = "empNo"/>
 8   <result column = "salary" property = "salary"/>
 9   <result column = "from_date" property = "fromDate"/>
10   <result column = "to_date" property = "toDate"/>
11   </resultMap>
12   <sql id = "salaryCols">
13     emp_no,salary,from_date,to_date
14   </sql>
15   <select id = "getSalaryByEmpNo" resultMap = "salary">
16     select <include refid = "salaryCols" /> from salaries where emp_no = #{empNo}
17   </select>
18 </mapper>

mybatis-config 加载:

<mapper resource="salary/mapper/SalaryMapper.xml"/>

下面是重点,这次我们还是在 resultMap 里做点手脚,但是不再是 association,一对多关联时,应该使用 collection:

<collection property = "salaries" column = "emp_no"
    select = "salary.dao.SalaryDao.getSalaryByEmpNo" />

属性作用和 association 一致,然后我们运行测试程序:

输出:

empNo : 111784 firstName : Marjo lastName : Giarratana deptNo : d009
salaries:
empNo: 111784 salary: 40000 from : Fri Feb 12 08:00:00 CST 1988 to : Sat Feb 11 08:00:00 CST 1989

empNo: 111784 salary: 41189 from : Sat Feb 11 08:00:00 CST 1989 to : Sun Feb 11 08:00:00 CST 1990

empNo: 111784 salary: 44026 from : Sun Feb 11 08:00:00 CST 1990 to : Mon Feb 11 08:00:00 CST 1991

empNo: 111784 salary: 48315 from : Mon Feb 11 08:00:00 CST 1991 to : Tue Feb 11 08:00:00 CST 1992

empNo: 111784 salary: 48079 from : Tue Feb 11 08:00:00 CST 1992 to : Wed Feb 10 08:00:00 CST 1993

empNo: 111784 salary: 48181 from : Wed Feb 10 08:00:00 CST 1993 to : Thu Feb 10 08:00:00 CST 1994

empNo: 111784 salary: 48273 from : Thu Feb 10 08:00:00 CST 1994 to : Fri Feb 10 08:00:00 CST 1995

empNo: 111784 salary: 49770 from : Fri Feb 10 08:00:00 CST 1995 to : Sat Feb 10 08:00:00 CST 1996

empNo: 111784 salary: 51900 from : Sat Feb 10 08:00:00 CST 1996 to : Sun Feb 09 08:00:00 CST 1997

empNo: 111784 salary: 52121 from : Sun Feb 09 08:00:00 CST 1997 to : Mon Feb 09 08:00:00 CST 1998

empNo: 111784 salary: 54187 from : Mon Feb 09 08:00:00 CST 1998 to : Tue Feb 09 08:00:00 CST 1999

empNo: 111784 salary: 54138 from : Tue Feb 09 08:00:00 CST 1999 to : Wed Feb 09 08:00:00 CST 2000

empNo: 111784 salary: 54183 from : Wed Feb 09 08:00:00 CST 2000 to : Thu Feb 08 08:00:00 CST 2001

empNo: 111784 salary: 57946 from : Thu Feb 08 08:00:00 CST 2001 to : Fri Feb 08 08:00:00 CST 2002

empNo: 111784 salary: 58189 from : Fri Feb 08 08:00:00 CST 2002 to : Fri Jan 01 08:00:00 CST 9999

其道理和 association 是一致的,这里我们成功的将获取到的工资信息装填到了 employee 的属性 salaries(List)中。

四、mybatis 小结

到目前为止,mybatis 的基础功能已经差不多结束了,mybatis 还有很多强大的功能,比如插件在以后再继续学习。本着由简到难的方式,mybatis 暂时告一段落,接下来是 SSM 框架的 spring 部分。

posted @ 2018-09-30 11:38  crazy_runcheng  阅读(276)  评论(0编辑  收藏  举报