一对多,是最常见的一种设计。就是 A 表的一条记录,对应 B 表的多条记录,且 A 的主键作为 B 表的外键。这主要看以哪张表为中心,下面的测试数据中,从employee 表来看,一个员工对应一个部门,是一对一关系,如果从部门角度来看,则是一对多的关系,一个部门对应多个员工,本节主要研究一对多的关系。

查询部门的时候将部门对应的所有员工信息也查询出来

数据表建立

新建数据表department,有两个字段,插入两条数据如下:

id
dept_name
1
CIA
2
FSB

新建数据表employee,有三个字段,其中dept_id是外键,关联department表的主键id。插入数据如下:

id
last_name
dept_id
1
Tom
1
2
Jerry
2
3
Neo
1
4
Cypher
2

新建maven工程,添加依赖,主要是mybatis和mysql

org.mybatis
mybatis
3.4.6
mysql
mysql-connector-java
5.1.47

编写数据库表对应的实体。

对于department表,对应实体如下:注意增加一个包含了Employee集合。

packagecom.yefengyu.mybatis.entity;importjava.util.List;public classDepartment
{privateInteger id;privateString deptName;private Listemployees;publicInteger getId()
{returnid;
}public voidsetId(Integer id)
{this.id =id;
}publicString getDeptName()
{returndeptName;
}public voidsetDeptName(String deptName)
{this.deptName =deptName;
}public ListgetEmployees()
{returnemployees;
}public void setEmployees(Listemployees)
{this.employees =employees;
}
@OverridepublicString toString()
{return "Department{" +
"id=" + id +
", deptName='" + deptName + '\'' +
", employees=" + employees +
'}';
}
}

对于employee表,实体如下,注意在Employee实体中把外键直接变成对于Department对象的引用。

packagecom.yefengyu.mybatis.entity;public classEmployee
{privateInteger id;privateString lastName;privateDepartment department;publicInteger getId()
{returnid;
}public voidsetId(Integer id)
{this.id =id;
}publicString getLastName()
{returnlastName;
}public voidsetLastName(String lastName)
{this.lastName =lastName;
}publicDepartment getDepartment()
{returndepartment;
}public voidsetDepartment(Department department)
{this.department =department;
}
@OverridepublicString toString()
{return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", department=" + department +
'}';
}
}

编写mapper接口

packagecom.yefengyu.mybatis.mapper;importcom.yefengyu.mybatis.entity.Department;public interfaceDepartmentMapper
{publicDepartment getDeptById(Integer id);
}

根据部门ID查询部门信息和对应的所有员工信息。

编写mapper映射文件(本节重点)

collection嵌套结果集方法:

/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name
from department d
left join employee e
on e.dept_id = d.id
where d.id = #{id}

新建一个mybatis全局配置文件,详细信息见官网

/p>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
测试
public static voidmain(String[] args)throwsIOException
{
InputStream resourceAsStream= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory= newSqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();try{
DepartmentMapper mapper= sqlSession.getMapper(DepartmentMapper.class);
Department dept= mapper.getDeptById(1);
System.out.println(dept);
}finally{
sqlSession.close();
}
}

结果如下:

Created connection 1938056729.
Setting autocommit tofalse on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]==> Preparing: select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name from department d left join employee e on e.dept_id = d.id where d.id = ?
==> Parameters: 1(Integer)<==Columns: id, last_name, dept_id, d_id, dept_name<== Row: 1, Tom, 1, 1, CIA<== Row: 3, Neo, 1, 1, CIA<== Total: 2Department{id=1, deptName='CIA', employees=[Employee{id=1, lastName='Tom', department=null}, Employee{id=3, lastName='Neo', department=null}]}
Resetting autocommit totrue on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]

上面查询虽然可以查询出数据,但是数据过多则会有性能问题,因此好的做法是分步查询。

分步查询

1、新增查询员工的接口,特别注意是根据部门id来查询

packagecom.yefengyu.mybatis.mapper;importcom.yefengyu.mybatis.entity.Employee;public interfaceEmployeeMapper
{
Employee getEmpByDeptId(Integer deptId);
}

2、编写对应的mapper映射文件

/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from employee wheredept_id = #{id}

3、编写查询部门的接口

packagecom.yefengyu.mybatis.mapper;importcom.yefengyu.mybatis.entity.Department;public interfaceDepartmentMapper
{publicDepartment getDeptById(Integer id);
}

4、编写对应的映射文件

/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select id ,dept_name from department where id = #{id}

5、测试结果

Created connection 1694556038.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
==> Preparing: select id ,dept_name from department where id = ?
==> Parameters: 1(Integer)<== Columns:id, dept_name
<==Row: 1, CIA
<==Total: 1==>Preparing: select * from employee where dept_id= ?
==>Parameters: 1(Integer)
<==Columns: id, last_name, dept_id
<==Row: 1, Tom, 1
<==Row: 3, Neo, 1
<==Total: 2
Department{id=1,deptName='CIA', employees=[Employee{id=1,lastName='Tom', department=null},Employee{id=3,lastName='Neo', department=null}]}Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]

如果测试代码的打印改为:

System.out.println(dept.getDeptName());

那么结果如下,不会查询员工信息。

Created connection 1694556038.
Setting autocommit tofalseon JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]==> Preparing: select id ,dept_name from department where id = ?
==> Parameters: 1(Integer)<==Columns: id, dept_name<== Row: 1, CIA<== Total: 1CIA
Resetting autocommit totrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Returned connection1694556038 to pool.

全局配置与局部配置

1、全局配置

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。

默认值:false

aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。

默认值:false (在 3.4.1 及之前的版本默认值为 true),现在新版本可以不用关注此设置。

2、局部配置fetchType

lazy:延迟

eager:立即

3、区别(查询部门信息,不查看员工信息时)

全局

局部

是否延迟

不开启

不开启

不开启

lazy

不开启

eager

开启

不开启

开启

lazy

开启

eager