一、准备工作
- 我们之前的操作都是针对一张表来完成的,如果需要处理多张存在关联关系的表 >> 高级映射
- 我们提前准备两张表:t_clazz、t_student
插入几条数据:
- 创建新模块:mybatis-010-advanced-mapping
- 修改pom配置文件【打包方式、添加依赖】
- 建包【pojo、utils、mapper、test】、copy那些固定的文件
- 创建pojo类两个、接口两个、映射文件两个、测试类两个
- 高级映射分为两种:多对一映射 和 一对多映射【谁在前谁为主表/类】
- 无论是一对多还是多对一,总是为多的一方添加外键
二、多对一映射
🌔 1、学生与班级之间的关系是怎样的呢?
所以我们可以得出结论:
(1)t_student 表是主表,Student类的实例是主对象
(2)t_clazz 表是副表, Clazz类的实例是副对象
(3)我们在Student类中添加Clazz类的对象,来模拟外键
🌔 2、我们如何实现这种多对一的表的查询呢?
- 第一种方式:一条SQL语句,级联属性映射。
- 第二种方式:一条SQL语句,association。
- 第三种方式:两条SQL语句,分步查询。【常用,可复用–支持懒加载】
🌔 3、如何实现级联属性映射呢?
- 老样子三个文件:接口方法、SQL语句、测试方法
(1)接口方法:参数只有我们想要查询的学生id,返回学生信息、附带班级信息
/**
* 根据id获取学生信息,同时获得学生关联的班级信息
* @param id 学号
* @return 包含班级对象的学生对象
*/
Student selectById(Integer id);
(2)SQL语句:使用了属性名和字段名的映射,基于班级号左外连接,筛选指定id的学生信息
<!--一条SQL语句,级联属性映射-->
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<result property="clazz.cid" column="cid" />
<result property="clazz.cname" column="cname" />
</resultMap>
<select id="selectById" resultMap="studentResultMap">
select
s.sid, s.sname, c.cid, c.cname
from
t_student s left join t_clazz c on s.cid = c.cid
where
s.sid = #{
sid}
</select>
(3)测试方法:就传个id,再打印一下学生信息
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectById(4);
System.out.println(student);
sqlSession.close();
}
🌔 4、如何使用 association 来完成映射呢?
- 属于一种关联映射与前一种类似,只需要修改 resultMap 中的信息即可
<resultMap id="studentResultMapAssociation" type="Student">
<!--Student类的映射-->
<id property="sid" column="sid"/>
<result property="sname" column="sname" />
<!--使用association标签
property我们提供映射POJO类的属性名
javaType用来指定要映射的Java类型
-->
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid" />
<result property="cname" column="cname" />
</association>
</resultMap>
- SQL中select语句的内容不变,给出测试程序
@Test
public void testSelectByIdAssociation(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectByIdAssociation(4);
System.out.println(student);
sqlSession.close();
}
🌔 5、如何通过分步查询来完成这种多对一的查询?
- 要写两条SQL语句,同时也要在两个接口中写方法
- 重点在于利用resultMap完成一条SQL调用另一条SQL,并完成查询结果的传递
(1)我们在两个接口中写的方法
// StudentMapper
/**
* 分步查询第一步
* @param id 学生id
* @return 获得学生对象
*/
Student selectByIdStep1(Integer id);
// ClazzMapper
/**
* 分步查询第二步
* @param id 班级号
* @return 班级对象
*/
Clazz selectByIdStep2(Integer id);
(2)我们在ClazzMapper.xml 中写的SQL语句【这部分简单,先给出】
<!--此处没有再写resultMap因为属性名和字段名一致-->
<select id="selectByIdStep2" resultType="Clazz">
select cid, cname from t_clazz where cid = #{
cid}
</select>
(3)我们在StudentMapper.xml 中写的SQL语句 【重点】
<!--分步查询-->
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="cid"/>
</resultMap>
<select id="selectByIdStep1" resultMap="studentResultMapByStep">
select sid, sname, cid from t_student where sid = #{
sid}
</select>
-
在resultMap中,刚开始还是正常给出属性名和列名之间的映射
-
在association标签中,property 仍然是我们需要映射的对象
-
select 属性代表我们要调用的SQL语句,后面为全限定类名+SQL的 id
-
colum 属性代表我们当前的查询得到的指定的值传递给我们要调用的SQL语句 【给第二步传值】
(4)编写测试方法:
@Test
public void testSelectByIdStep1(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectByIdStep1(5);
System.out.println(student);
sqlSession.close();
}
🌔6、分步查询有什么优点?
-
SQL语句的可复用强:因为我们可以单独使用两个片段 >> 可复用
-
支持延迟加载 >> 开启延迟加载后,是否调用指定的SQL语句就看是否使用了那条SQL语句查询的结果
-
那么我们如何开启延迟加载呢?
-
我们只需要在调用其他SQL语句的association标签中添加一个属性:
fetchType="lazy"
-
开启后可以提高mybatis的效率
-
当前的延迟加载只针对当前的SQL有效,我们也可以直接开启全局的延迟加载
在核心配置文件的 settings / setting 标签下配置
<settings>
<setting name="lazyLoadingEnabled" value="true">
</settings>
- 开启全局延迟加载后,所有分步查询都会自动启用延迟加载,加入当前的resultMap不想用延迟加载怎么办?
我们还是在 association 的标签中通过 fetchType 属性来取消延迟加载,属性值为 eger
三、一对多映射
🌔 1、一对多的映射原理是什么呢?
- 就是说我们一个班级可以有很多个学生,一个学生只能属于一个班级
- 之前我们说过,谁在前谁为主表,所以 t_clazz 表是主表,t_student 副表
- Clazz的对象是一个集合,存储所有班级内的学生信息
- 我们可以通过两种方式来实现这种一对多的映射:Collection、分步
🌔 2、如何通过collection标签来完成映射?
(1)我们需要修改Clazz类,添加一个stus的Student集合的私有属性【并修改对应的配置信息】
private List<Student> stus;
(2)在ClazzMapper接口中设计方法
Clazz selectByCollection(Integer id);
(3)在ClazzMapper.xml文件中设计对应的resultMap和SQL语句
<!--在写一个resultMap,先根据班级号查询,再调用学生的表查询-->
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid" />
<result property="cname" column="cname" />
<!--一对多
ofType 代表集合中元素类型
property 代表我们的集合
-->
<collection property="stus" ofType="student">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
</collection>
</resultMap>
<select id="selectByCollection" resultMap="clazzResultMap">
select c.cid, c.cname, s.sid, s.sname from t_clazz c left join t_student s on c.cid = s.cid where c.cid = #{
cid}
</select>
- 感觉和多对一的第二个方法有点类似
- 通过集合标签,指定我们要处理的集合和对应的类型
(4)测试方法:
@Test
public void testSelectByCollection(){
SqlSession sqlSession = SqlSessionUtil.openSession();
ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
Clazz clazz = mapper.selectByCollection(1001);
System.out.println(clazz);
sqlSession.close();
}
🌔 3、如何通过分步的方法实现映射?
(1)写两个接口方法
// ClazzMapper
Clazz selectByStep1(Integer cid);
//StudentMapper
List<Student> selectByCidStep2(Integer cid);
(2)对应映射文件的SQL:
// ClazzMapper.xml
<resultMap id="clazzResultMapStep" type="Clazz">
<id property="cid" column="cid" />
<result property="cname" column="cname" />
<collection property="stus"
select="com.powernode.mybatis.mapper.StudentMapper.selectByCidStep2"
column="cid"/>
</resultMap>
<select id="selectByStep1" resultMap="clazzResultMapStep">
select * from t_clazz where cid = #{
cid}
</select>
// StudentMapper.xml
<select id="selectByCidStep2" resultType="Student">
select sid, sname from t_student where cid = #{
cid}
</select>
(3)测试方法:
@Test
public void testSelectByStep1(){
SqlSession sqlSession = SqlSessionUtil.openSession();
ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
Clazz clazz = mapper.selectByStep1(1000);
System.out.println(clazz);
sqlSession.close();
}
(4)这种分步方法也支持延迟加载,可以局部配置也可以全局配置