概述
存在数据库表:
--班级表
create table t_clazz(
id int(12) not null auto_increment,
cla_name varchar(64) not null ,
primary key (id)
);
--学生表
create table t_student(
id int(12) not null auto_increment,
stu_name varchar(64) not null ,
phone_num int(13),
cla_id int(12) not null,
add constraint foreign key (cla_id) references t_clazz(id),
primary key (id)
);
--热水卡表
create table t_water_card(
id int(12) not null auto_increment,
stu_id int(12) not null ,
balance double,
constraint foreign key (stu_id) references t_student(id),
primary key (id)
);
mybtis支持延迟加载,在说延迟加载之前,我们先了解为什么需要延迟加载?当我们对单个数据库表操作时,比如有一个热水卡类HotWaterCardt如下代码:
public class HotWaterCard{
private long id;
private long stuId;
private long balance;
private Student student;
/****setter and getter****/
}
很显然,水卡和学生是一对一的级联关系,若采用mybatis的级联操作(代码如下)来获取HotWaterCard的数据,那么就会执行getStudentById的sql语句,查询出student数据。当我们只是要查看id为1的热水卡的余额,并不需要对应的学生信息时,很显然这样的操作会造成性能的浪费。因此,我们可以通过使用延迟加载,在需要student数据的时候再加载student的数据。这就是延迟加载的目的。(当然,这里用例的级联相当少,实际上对性能不会有太大的影响,但是当级联很多时,那加载不需要的数据过多就会造成很大的性能浪费问题)
<resultMap id="hotWaterCard" type="edu.ssm.pojo.HotWaterCard">
<id column="id" property="id"/>
<result column="stu_Id" property="stuId"/>
<result column="balance" property="balance"/>
<!--一对一级联操作
将stu_id传给StudentMapper的getStudentById方法,返回给HotWaterCard的student属性-->
<association property="student" column="stu_id" select="edu.ssm.mapper.StudentMapper.getStudentById"/>
</resultMap>
其他pojo
public class Student{
private long id;
private long claId;
private String stuName;
private String phoneNum;
/****setter and getter****/
}
public class Clazz{
private int id;
private String claName;
private List<Student> studentList;
/****setter and getter****/
}
级联
那么级联又是什么?级联是数据库实体的概念。以上述三个实体,热水卡HotWaterCard、学生student和班级clazz为例,一个学生只有一张热水卡,一张热水卡只能被一个学生持有,因此学生和热水卡是一对一级联;一个学生只有一个班级,一个班级有多个学生,因此是一对多的关系。mybatis还有一种叫鉴别器的级联,它是一种可以选择具体实现类的级联。
在mybatis中,级联是在mapper.xml文件中resultMap属性中配置的。三种级联属性分别对应上述的三种级联。
- association:一对一级联。
- collection:一对多级联。
- discriminator:鉴别器。
在mybatis中,association的用法即上面热水卡和学生的配置。下面我们看collection的用法。
<resultMap id="clazz" type="edu.ssm.pojo.Clazz">
<id column="id" property="id"/>
<result column="cla_name" property="claName"/>
<!--一对多级联
将班级clazz的id属性传给StudentMapper的getStudentListByClaId,根据claId获取学生列表-->
<collection property="student" column="id" select="edu.ssm.mapper.StudentMapper.getStudentListByClaId"/>
</resultMap>
其中,getStudentListByClaId的返回值类型是Student或者是List<Student>都可以,只要保证Clazz中的student属性是List<Student>就行。
缓存
在概述中,我们使用了一个例子来说明为什么要使用延迟加载,现在用一种更为正式的方式讲解一下。
级联操作中会产生N+1的问题。N+1问题指的是,假设现在在有n个关联关系完成了级联,那么只要再加入一个关联关系,就变成了N+1个级联,所有的级联SQL都会被执行,同时会有很多不是我们关心的数据被取出,这样会造成很大的资源浪费,这就是N+1问题。mybatis提供了延迟加载功能来应对N+1问题。
配置延迟加载可以采用全局设置和局部设置的方式。
全局设置在setting中设置属性lazyLoadingEnable(全局延迟加载开关,true为开启)为true, aggressiveLazyLoading(层级加载开关,false为不开启)为false。
<settings>
<!--lazyLoadingEnabled:决定是否开始延迟加载-->
<!--aggressiveLazyLoading:决定是否采用层级加载-->
<!--在级联操作中,最优的性能选择是lazyLoadingEnabled为true,aggressiveLazyLoading为false。
但是以上设置为全局设置,会影响全部级联,并不能完全满足业务需求。例如,在同一个层级中有4个表,
但业务只需要用到其中一个,若采用全局的aggressiveLazyLoading设置不采用层级加载,
则导致需要用到的表也被延迟加载,而采用层级加载则会多加载3个无用的表数据影响性能。
因此,可以在association或collection中加入属性fetchType=eager或lazy,
eager表示马上加载,lazy表示延迟加载。
fetchType属性会忽视全局配置项lazyLoadingEnabled和aggressiveLazyLoading。
从而可以选择同一个层级中某一些表立马加载,某一些表延迟加载。-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
局部采用在association和collection中设置fetchType(fetchType属性会忽视全局配置项lazyLoadingEnabled和aggressiveLazyLoading),有如下两个值:当值为eager时,遇到当前pojo后,立即加载对应的数据,当值为lazy时,则延迟加载。
<resultMap id="hotWaterCard" type="edu.ssm.pojo.HotWaterCard">
<id column="id" property="id"/>
<result column="stu_Id" property="stuId"/>
<result column="balance" property="balance"/>
<!--一对一级联操作
将stu_id传给StudentMapper的getStudentById方法,返回给HotWaterCard的student属性-->
<association property="student" column="stu_id" select="edu.ssm.mapper.StudentMapper.getStudentById" fetchType="lazy"/>
</resultMap>
<resultMap id="clazz" type="edu.ssm.pojo.Clazz">
<id column="id" property="id"/>
<result column="cla_name" property="claName"/>
<!--一对多级联
将班级clazz的id属性传给StudentMapper的getStudentListByClaId,根据claId获取学生列表-->
<collection property="student" column="id" select="edu.ssm.mapper.StudentMapper.getStudentListByClaId" fetchType="eager"/>
</resultMap>