目录
三、choose/when/otherwise标签 (不常用)
多对多关联映射 Student练习:多表连接、分步查询、延迟加载
映射关联关系
在实际开发中,经常会将来自多张表的数据在一个位置显示。比如查询并显示的员工信息中会有来自部门表、岗位表的数据,而后台一般只定义一个方法:
List<User> findUser(conditions); 这就要求User中要包含部门Dept、岗位Position的信息;
在MyBatis中是如何实现对多表的查询并实现数据的组装呢?(此处就没有DML操作什么事了)。 主要可以通过两种方式来实现,以顾客表Customer和订单表Order为例进行讲解。
(1)连接查询:只需执行一条SQL语句,效率高,不灵活,只有立即加载,没有延迟加载。
(2)分步查询:需执行多条SQL语句,效率低,灵活,可设置立即还是延迟。
一、关联关系概念说明
① 数量关系
主要体现在数据库表中,不管哪种关系,都是通过外键搞定。
- 一对一:人和身份证 (需要给外键加唯一约束,将一对一变成一对多)
- 一对多:部门和员工,客户和订单
- 多对多:学生和课程,老师和学生 (需要引入一个中间表)
② 关联关系的方向 (待修改,例子改为员工部门)
- 双向:双方都可以访问到对方
Customer:包含Order的集合属性
Order:包含单个Customer的属性 - 单向:双方中只有一方能够访问到对方
Customer:不包含Order的集合属性,访问不到Order
Order:包含单个Customer的属性、 - 数据库之间建立关联关系通过外键
- 实体类之间建立关联关系通过成员变量
二、关键词总结
关联关系 | 配置项关键词 | 所在配置文件 |
---|---|---|
对一 | association 标签/ javaType 属性 | Mapper配置文件中的 resultMap |
对多 | collection 标签/ ofType 属性 | Mapper配置文件中的 resultMap |
对一分步 | association 标签/ select 属性 | Mapper配置文件中的 resultMap |
对多分步 | collection 标签/ select 属性 | Mapper配置文件中的 resultMap |
延迟加载[低] | lazyLoadingEnabled 设置为true aggressiveLazyLoading 设置为false | Mybatis全局配置文件中的 settings |
延迟加载[高] | lazyLoadingEnabled 设置为true | Mybatis全局配置文件中的 settings |
三、创建模型
①创建数据库表插入测试数据
CREATE TABLE `t_customer` (
`customer_id` INT NOT NULL AUTO_INCREMENT,
`customer_name` CHAR(100),
PRIMARY KEY (`customer_id`)
);
CREATE TABLE `t_order` (
`order_id` INT NOT NULL AUTO_INCREMENT,
`order_name` CHAR(100),
`customer_id` INT,
PRIMARY KEY (`order_id`)
);
INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1');
②创建实体类
//顾客类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
private Integer customerId; //客户编号
private String customerName;//客户姓名
//客户信息中可以知道该客户的所有订单
private List<Order> orderList = new ArrayList<>();
}
//订单类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private Integer orderId;
private String orderName;
//一般保留,添加使用
private Integer customerId;
//订单知道所属的客户详细信息,查询使用
private Customer customer;
}
实际开发时,一般在开发过程中,不给数据库表设置外键约束。
原因是避免调试不方便。
一般是功能开发完成,再加外键约束检查是否有bug。
③Mapper接口和映射文件
Mapper.xml 需要填写命名空间,Mapper接口全类名。
mybatis_config.xml全局配置需要指定映射文件路径。
mybatis-config.xml中注册Mapper配置文件
<mappers>
<!-- 指定映射文件的路径 -->
<mapper resource="mapper/CustomerMapper.xml"/>
<mapper resource="mapper/OrderMapper.xml"/>
</mappers>
四、连接查询 (JOIN多表查询)
1、一对一 & 多对一
查询某个订单的信息,携带客户的信息。(多对一)
关键词:association 、javaType
注意1:使用join连接查询,即使属性名和字段相同,映射也不能少
注意2:使用join连接查询,配置文件中配置了驼峰命名映射,映射也不能少
注意3:关联的映射要使用 association
注意4:javaType="com.atguigu.pojo.Customer" 可以省略
Mapper接口
public interface OrderMapper {
//查询指定编号的订单信息(携带客户的信息)
Order selectOrderWithCustomer(Integer orderId);
}
Mapper映射文件
<mapper namespace="com.atguigu.mapper.OrderMapper">
<select id="selectOrderWithCustomer" resultMap="orderMap">
SELECT *
FROM t_order o
JOIN t_customer c
ON o.customer_id = c.customer_id
WHERE o.order_id = #{orderId}
</select>
<resultMap id="orderMap" type="order">
<id column="order_id" property="orderId"></id>
<result column="order_name" property="orderName"></result>
<result column="customer_id" property="customerId"></result>
<!--关键 关联的映射--> <!--javaType可以省略-->
<association property="customer" javaType="com.atguigu.pojo.Customer">
<id column="customer_id" property="customerId"></id>
<result column="customer_name" property="customerName" ></result>
</association>
</resultMap>
</mapper>
测试类
@Test
public void testSelectOrderWithCustomer(){
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
Order order = mapper.selectOrderWithCustomer(1);
System.out.println(order);
System.out.println(order.getCustomer());
}
2、一对多 & 多对多(集合)
查询某个客户的信息,携带其多个订单信息 ( 一对多 )
关键词:collection 、 ofType
Mapper接口
public interface CustomerMapper {
//查询某个客户的信息(携带其多个订单信息)
Customer selectCustomerWithOrderList(Integer customerId);
}
Mapper映射文件
<mapper namespace="com.atguigu.mapper.CustomerMapper">
<!--查询某个客户的信息(携带其多个订单信息)-->
<select id="selectCustomerWithOrderList" resultMap="customerMap">
SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
FROM t_customer c
JOIN t_order o
ON c.customer_id = o.customer_id
WHERE c.customer_id = #{customerId}
</select>
<resultMap id="customerMap" type="customer">
<id column="customer_id" property="customerId"></id>
<result column="customer_name" property="customerName"></result>
<collection property="orderList" ofType="order"><!--别名-->
<id column="order_id" property="orderId"></id>
<result column="order_name" property="orderName"></result>
</collection>
</resultMap>
</mapper>
测试类
//查询某个客户的信息(携带其多个订单信息)
@Test
public void testSelectCustomerWithOrderList(){
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.selectCustomerWithOrderList(1);
System.out.println(customer);
System.out.println(customer.getOrderList());
}
总结:
多表连接查询的优点:
- 一条SQL语句查询多张表,结果包含多张表的数据
- 速度快
多表连接查询的缺点:
- 如果只想获取一张表的数据,但是多张表也会查询出来(效率低)
- 对于一对多来说,这个问题更严重。只要客户名称,不要订单列表,却把订单列表(这是一个集合,多条记录)也查出来。
解决:
- 多写几个Sql,完成不同的功能。
- 可以采用更加灵活的延迟加载。要使用延迟加载,必须先使用分步查询。
五、分步查询
连接查询缺点:不灵活。只有立即加载,没有延迟加载。数据不管是否需要,都会查询出来。
解决:如果不确定是否需要,可以采用更加灵活的延迟加载。要使用延迟加载,必须先将连接查询 (一条SQL) 拆分为分步查询 (多条SQL语句)
案例:查询客户信息,可能要查看订单信息,也可能不查看订单信息.
准备1:查询指定编号的客户(不带订单 单表查询)
public interface CustomerMapper {
//单表查询
Customer findById(Integer customerId);
}
<!--分布查询,查询客户--><!--使用了驼峰命名自动匹配-->
<select id="findById" resultType="customer">
SELECT *
FROM t_customer
WHERE customer_id = #{customerId}
</select>
@Test
public void testFindById(){
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.findById(1);
System.out.println(customer);
}
准备2:查询指定编号的客户的订单 (只有订单,单表)
public interface OrderMapper {
//查询指定编号的订单信息(携带客户的信息)
Order selectOrderWithCustomer(Integer orderId);
}
<!--分布查询,查询用户订单--><!--使用了驼峰命名自动匹配-->
<select id="selectOrderList" resultType="order">
SELECT *
FROM t_order
WHERE customer_id = #{customerId}
</select>
//分布查询准备: 查询指定客户的所有订单(单表)
@Test
public void testSelectOrderList(){
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orders = mapper.selectOrderList(1);
orders.forEach(order -> System.out.println(order));
}
注意:
1.上面进行了两个单表查询,使用的是ResultType,而不是ResultMap
2.单表查询,使用自动驼峰命名规则可以简化操作,不用手动别名。
<setting name="mapUnderscoreToCamelCase" value="true"/>
实现以上两个单独操作的合并
select id="findById2" resultMap="customerMap2">
SELECT *
FROM t_customer
WHERE customer_id = #{customerId}
</select>
<resultMap id="customerMap2" type="customer">
<id column="customer_id" property="customerId"></id>
<result column="customer_name" property="customerName"></result>
<collection select="com.atguigu.mapper.OrderMapper.selectOrderList"
column="customer_id"
property="orderList"></collection>
</resultMap>
分步查询是延迟加载的前提。
六、延迟加载(懒加载)
延迟加载:对于实体类关联的属性到需要使用时才查询。也叫 懒加载。
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
- 第一步:开启延迟加载(lazyload 懒加载)全局开关并测试
注意:由于分开关的优先级高于总开关,总开关为false,分开关 fetchType="lazy" 也可以开启懒加载。lazy | eager
mybatis-config.xml
<settings>
<!--配置懒加载总开关 默认值false不开启-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
@Test
public void testFindById2(){
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = mapper.findById2(1);
//System.out.println(customer);
//不执行第二步查询,只查询了客户信息
System.out.println(customer.getCustomerName());
System.out.println("--------------------------");
//只有需要获取订单信息时,才会发起第二步查询
System.out.println(customer.getOrderList());
}
第二步:开启分开关:fetchType 并测试
resultMap 映射中加入参数 fetchType="eager" ,立即查询,饥饿加载。执行所有的分步查询。
<!--分步查询-->
<select id="findById2" resultMap="customerMap2">
SELECT *
FROM t_customer
WHERE customer_id = #{customerId}
</select>
<resultMap id="customerMap2" type="customer">
<id column="customer_id" property="customerId"></id>
<result column="customer_name" property="customerName"></result>
<collection select="com.atguigu.mapper.OrderMapper.selectOrderList"
column="customer_id"
property="orderList"
fetchType="eager"
></collection>
</resultMap>
总开关设置false,分开关设置 fetchType="lazy",开启懒加载,分布查询。
select = "com.atguigu.mapper.OrderMapper.selectOrderList" 指定要引用的SQL语句
column = "customer_id" 建立关联关系时所依赖的字段
property = "orderList" 查询的成员变量
fetchType = "eager" 懒加载分开关
七、多对多关联和一对一关联
- 多对多
1、在数据库中,通过引入一个中间表,将一个多对多变成两个一对多
2、在Java类中
方案一:可以象数据库表一样,定义三个实体类
方案二:还可以在Java类中直接建立多对多关系 (List集合),映射文件中均使用Collection进行映射。
- 一对一
1、数据库中使用外键搞定
方案1:外键关联:将外键同时设为unique,将一对多变成了一对一
方案2:主键关联:同时是主键和外键。主键保证了唯一和非空;外键保证了必须参考Person类的主键。不允许现有身份证号,再有人。
2、实体类中
在MyBatis中如何表示一对一;
Person类: IdCard idcard;
IdCard类:Person person;
两端的映射文件中都使用association。
多对多加集合 ,一对一加对象
动态SQL语句
经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索,比如OA系统中的支出查询等。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?
在使用JABC中,一般情况下是使用StringBuilder类及其append方法实现。利用动态 SQL 这一特性可以彻底避免字符串频繁拼接的痛苦。
MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。动态SQL语句不需要拼接代码,只需要在xml映射文件中进行配置,利于修改。
MyBatis也可以在注解中配置SQL,但是由于注解功能受限,尤其是对于复杂的SQL语句,可读性很差,所以较少使用。
一、if 和 where标签(重点)
//if语句
public List<Employee> findEmp(@Param("empName") String empName, @Param("minSalary") Double minSalary);
//where语句
public List<Employee> findEmp2(@Param("empName") String empName, @Param("minSalary") Double minSalary);
<!-- if 1=1 避免一个第一个条件没有触发,and报错-->
<select id="findEmp" resultType="employee">
select * from t_emp where 1=1
<if test="empName!=null and empName!=''">
and emp_name like "%"#{empName}"%"
</if>
<if test="minSalary>0">
and emp_salary >= #{minSalary}
</if>
</select>
<!--where 会自动去掉多余的and / or-->
<select id="findEmp2" resultType="employee">
select * from t_emp
<where>
<if test="empName!=null and empName!=''">
and emp_name like "%"#{empName}"%"
</if>
<if test="minSalary>0">
and emp_salary >= #{minSalary}
</if>
</where>
</select>
小技巧:where 1= 1 让其他条件都不是第一个条件,都以and开始
二、trim标签 (了解)
使用trim标签控制条件部分两端是否包含某些字符
prefix 属性:指定要动态添加的前缀
suffix 属性:指定要动态添加的后缀
prefixOverrides 属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
suffixOverrides 属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
public List<Employee> findEmp3(@Param("empName") String empName, @Param("minSalary") Double minSalary);
//trim
@Test
public void testFindEmp3(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//List<Employee> emp = mapper.findEmp(null, 0.0);
List<Employee> emp = mapper.findEmp2("李", 1000.0);
emp.forEach(employee -> System.out.println(employee));
}
<!--trim-->
<select id="findEmp3" resultType="employee">
select * from t_emp
<trim prefix="where" prefixOverrides="and">
<if test="empName!=null and empName!=''">
and emp_name like "%"#{empName}"%"
</if>
<if test="minSalary>0">
and emp_salary >= #{minSalary}
</if>
</trim>
</select>
三、choose/when/otherwise标签 (不常用)
不管有几个条件,最多满足一个;如果一个也不满足,执行otherwise的条件。
//4.choose/when/otherwise标签(switch case)
public List<Employee> findEmp4(@Param("empName") String empName, @Param("minSalary") Double minSalary);
<!--choose-->
<select id="findEmp4" resultType="employee">
select * from t_emp where
<!--相当于switch case,不管有多少分支,最多执行一个-->
<choose>
<when test="empName!=null and empName!=''">
emp_name like "%"#{empName}"%"
</when>
<when test="minSalary>0">
emp_salary >= #{minSalary}
</when>
<otherwise>
1=1 <!--防止条件都不满足,where多余-->
</otherwise>
</choose>
</select>
四、foreach标签
注意关于foreach标签的collection属性。
如果数组参数没有使用@Param,collection使用 array;
如果集合参数没有使用@Param,collection使用list、collection;
//5.foreach
public List<Employee> findEmp5(@Param("idArr") int [] idArr);
public List<Employee> findEmp6(@Param("idList")List<Integer> idList);
//注意,没有使用@Param
public void saveEmps(List<Employee> empList);
<!--foreach-->
<!--传参数组-->
<select id="findEmp5" resultType="employee">
SELECT * FROM t_emp
WHERE emp_id IN
<!--没有指定@Param,使用array-->
<foreach collection="idArr" separator="," open="(" close=")" item="elem">
#{elem}
</foreach>
</select>
<!--传参集合-->
<select id="findEmp6" resultType="employee">
SELECT * FROM t_emp
WHERE emp_id IN
<!--没有指定@Param,使用collection、list-->
<foreach collection="idList" separator="," open="(" close=")" item="elem">
#{elem}
</foreach>
</select>
<!--批量添加-->
<insert id="saveEmps">
INSERT INTO t_emp
<!--没有指定@Param,使用collection、list-->
<foreach collection="list" separator="," open="VALUES" item="emp">
(null,#{emp.empName},#{emp.empSalary})
</foreach>
</insert>
//foreach
@Test//传数组
public void testFindEmp5(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
int[] idArr = {2,11,17,100};
List<Employee> emp = mapper.findEmp5(idArr);
emp.forEach(employee -> System.out.println(employee));
}
@Test//传集合
public void testFindEmp6(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Integer> idList = new ArrayList<>();
Collections.addAll(idList,2,11,17,100);
List<Employee> emp = mapper.findEmp6(idList);
emp.forEach(employee -> System.out.println(employee));
}
@Test//批量添加
public void testSaveEmps(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
ArrayList<Employee> empList = new ArrayList<>();
empList.add(new Employee(null,"白鼠1",123.0));
empList.add(new Employee(null,"白鼠2",123.0));
empList.add(new Employee(null,"白鼠3",123.0));
mapper.saveEmps(empList);
}
五、set标签
实际开发时,对一个实体类对象进行更新。往往不是更新所有字段,而是更新一部分字段。
针对update操作,会自动的去掉多余的 ","
public int updateEmp(Employee emp);
<!--set-->
<update id="updateEmp">
UPDATE t_emp
<set>
<if test="empName!=null and empName!=''"><!--注意逗号要加上,没用会自动去掉-->
emp_name = #{empName},
</if>
<if test="empSalary!=null and empSalary>0">
emp_salary = #{empSalary}
</if>
</set>
WHERE emp_id = #{empId}
</update>
@Test
public void testUpdateEmp(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = new Employee(25, null , 1000.0);
mapper.updateEmp(emp);
}
每年旅游要用
六、sql标签(了解)
提取SQL语句的公共部门,并使用include标签来引用。便于修改。
企业实际开发中,使用最多的其实是resultMap
<sql id="empColumns">
emp_id empId,emp_name empName,emp_salary empSalary
</sql>
调用
<select id="findEmp" resultType="employee">
select <include refid="empColumns"/> from t_emp
</select>
多对多关联映射 Student练习:多表连接、分步查询、延迟加载
- 预备:创建数据库表学生表、课程表、学生选课表并添加测试数据
- 功能1:查询所有学生及其选课信息(Student中含所选择的Course信息)
- 功能2:查询某一门课程及其选课的学生(Course中含选这门课的Student信息)
- 使用级联(分步)查询和多表连接查询分别完成
提示:在Student、Course中分别添加List类型的成员变量,并且在映射文件中分别使用<collection>元素进行配置。
一、准备工作、数据库表、实体类
项目结构:
学生类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer studentId;
private String studentName;
private Integer studentAge;
//学生选课信息
private List<Course> courseList;
}
课程类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
private Integer courseId;
private String courseName;
//学生信息
private List<Student> studentList;
}
学生表
选课表 (连接学生、课程表)
课程表
二、 多表连接方式
1. 查询所有学生及其选课信息
<!--命名空间,必须是Mapper接口的完整路径-->
<mapper namespace="com.atguigu.mapper.StudentMapper">
<!--多表连接查询 所有学生及其选课-->
<select id="findStudentAll" resultMap="studentList">
SELECT st.student_id,student_name,student_age,co.course_id,co.course_name
FROM h_student st
JOIN h_select se
ON st.student_id = se.student_id
JOIN h_course co
ON se.course_id = co.course_id
</select>
<resultMap id="studentList" type="student">
<id column="student_id" property="studentId"></id>
<result column="student_name" property="studentName"></result>
<result column="student_age" property="studentAge"></result>
<collection property="courseList" ofType="course">
<id column="course_id" property="courseId"></id>
<result column="course_name" property="courseName"></result>
</collection>
</resultMap>
public interface StudentMapper {
//多表连接查询
List<Student> findStudentAll();
//分表查询:查询所有学生及其选课
@Test
public void testFindStudentAll(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentAll = mapper.findStudentAll();
studentAll.forEach(student -> System.out.println(student));
}
2. 查询某一门课程及其选课的学生
<!--命名空间,必须是Mapper接口的完整路径-->
<mapper namespace="com.atguigu.mapper.CourseMapper">
<!--多表连接查询 指定课程下的所有学生-->
<select id="findCourseById" resultMap="courseList">
SELECT co.course_id,co.course_name,st.student_id,st.student_name,st.student_age
FROM h_course co
JOIN h_select se
ON co.course_id = se.course_id
JOIN h_student st
ON st.student_id = se.student_id
WHERE co.course_id = #{courseId}
</select>
<resultMap id="courseList" type="course">
<id column="course_id" property="courseId"></id>
<result column="course_name" property="courseName"></result>
<collection property="studentList" ofType="student">
<id column="student_id" property="studentId"></id>
<result column="student_name" property="studentName"></result>
<result column="student_age" property="studentAge"></result>
</collection>
</resultMap>
public interface CourseMapper {
//多表连接查询
List<Course> findCourseById(Integer id);
//分表查询:查询某一门课程及其学生
@Test
public void testFindCourseAll(){
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
List<Course> courseAll = mapper.findCourseById(2);
courseAll.forEach(course -> System.out.println(course));
}
三、分布查询、延迟加载方式
1. 查询所有学生及其选课信息
StudentMapper.xml
<!--分步查询 每个学生的课程-->
<select id="findStudentAll2" resultMap="findStudents">
SELECT *
FROM h_student
</select>
<resultMap id="findStudents" type="student">
<id column="student_id" property="studentId"></id>
<result column="student_name" property="studentName"></result>
<result column="student_age" property="studentAge"></result>
<collection select="com.atguigu.mapper.CourseMapper.findCourse"
column="student_id"
property="courseList"
fetchType="lazy"><!--开启懒加载(延迟加载)-->
</collection>
</resultMap>
CourseMapper.xml
<!--分步查询的分表 每个学生的课程-->
<select id="findCourse" resultType="course">
SELECT co.course_id,co.course_name
FROM h_course co
JOIN h_select se
ON co.course_id = se.course_id
WHERE se.student_id = #{studentId}
</select>
public interface StudentMapper {
//分步查询 每个学生的课程
List<Student> findStudentAll2();
}
//分步查询、延迟加载,每个学生和所选的课程
@Test
public void testFindStudent(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentAll = mapper.findStudentAll2();
for (Student student : studentAll) {
System.out.println(student.getCourseList());
System.out.println(student.getStudentName());
//SELECT * FROM h_student 懒加载
}
}
2. 查询某一门课程及其选课的学生
CourseMapper.xml
<!--分步查询 查询指定课程下的所有学生-->
<select id="findCourseById2" resultMap="findCourseByIds">
SELECT *
FROM h_course
WHERE course_id = #{courseId}
</select>
<resultMap id="findCourseByIds" type="course">
<id column="course_id" property="courseId"></id>
<result column="course_name" property="courseName"></result>
<collection select="com.atguigu.mapper.StudentMapper.findStudentById"
column="course_id"
property="studentList">
<!--开启了全局延迟加载 不用写 fetchType="lazy"-->
</collection>
</resultMap>
StudentMapper.xml
<!--分步查询分表 查询指定课程下的所有学生-->
<select id="findStudentById" resultType="student">
SELECT st.student_id,st.student_name,st.student_age
FROM h_student st
JOIN h_select se
ON st.student_id = se.student_id
WHERE course_id = #{courseId}
</select>
public interface CourseMapper {
//分步查询 查询指定课程下的所有学生
List<Course> findCourseById2(Integer id);
//分步查询、延迟加载 查询指定课程下的所有学生
@Test
public void testFindCourseById2(){
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
List<Course> courseAll = mapper.findCourseById2(2);
for (Course course : courseAll) {
/*List<Student> studentList = course.getStudentList();
System.out.println(studentList);*/
System.out.println(course.getCourseName());
//SELECT * FROM h_course WHERE course_id = ? 懒加载
}
}