mybatis 框架 resultMap 的延迟加载功能的实现
为什么需要延迟加载?
在我们的实际开发中,如果单表查询能满足业务需求。尽量用单表查询,因为单表查询的效率比多表关联查询快。那么当业务需求需要用到多表查询,我们应该怎么办呢?Mybatis 引入了延迟加载的概念。
什么叫延迟加载?
为了满足业务需求,必须进行多表查询时,可以先进行单表查询 ,在业务信息中需要用到关联表信息的时候,再进行关联表的单表查询。resultMap可以实现高级映射(使用association、collection实现一对一及其一对多),association、collection具有延迟加载的功能。
在SqlMapConfgi.xml中配置延迟加载开关:
<!--开启延迟加载的总开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--开启按需加载的开关 -->
<setting name="aggressiveLazyLoading" value="false"/>
需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时候,把对用户信息的按需去查询就叫做延迟加载。
延迟加载:先从单表查询,需要时再从关联表去查询,大大提高数据库的性能,因为单表查询要比多表关联查询速度要快。
使用association实现延迟加载
需求:
查询订单并且关联查询用户信息
延迟加载的resultMap的配置
使用association中的select指定延迟加载执行的statement的id
<!-- 延迟加载的resultMap -->
<resultMap type="com.wbs.domain.Orders"id="findOrdersUserLazyLoading">
<!-- 对订单信息进行映射配置 -->
<id column="id" property="id"/>
<result column="user_id"property="user_id" />
<result column="numbers"property="number" />
<result column="createtime"property="createtime" />
<result column="note"property="note" />
<!-- 实现对用户信息延迟加载
要使用mapper.xml的findUserById完成用户的id(user_id)完成用户信息的查询,如果
它不在本mapper中,前边需要加namespace
select:指定延迟加载所需要执行的statement的id(是根据user_id查询用户信息的statement)
column:订单信息中关联用户信息查询的列,是user_id
关联查询的SQL可以理解为select orders.*, (select username
fromusers where users.id=orders.user_id)username ,
(selectsex from users where users.id=orders.user_id)sex
fromorders
-->
<association property="user"javaType="com.wbs.domain.User"
select="com.wbs.mapper.UserMapper.findUserById"column="user_id">
</association>
</resultMap>
Mapper.xml
需要定义两个mapper的方法定义statement
1. 只查询订单信息
Select * from orders
2. 在查询订单的statement中使用association去延迟加载(执行)下边的statement(关联查询用户信息)
<!-- 查询订单,关联查询用户信息,用户信息通过延迟加载 -->
<select id="findOrdersUserLazyLoading"resultMap="findOrdersUserLazyLoading">
select* from orders
</select>
3. 关联查询用户信息
a) 通过上边查询到的订单信息中的user_id去关联查询用户信息。
<select id="findUserById"parameterType="int" resultType="user">
select * from users where id=#{value}
</select>
上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候,再执行findUserById,通过resultMap的定义将延迟加载执行配置起来。
Mapper.java
//查询订单,关联查询用户,用户信息是延迟加载
public List<Orders>findOrdersUserLazyLoading() throwsException;
测试:
1. 执行上边mapperde的findOrderUserLazyLoading方法,内部调用com.wbs.mapper.OrdersMapperCustomer的findOrderUserLazyLoading方法只查询orders信息(单表)
2. 在程序中遍历上一步查询出的List<Orders>,当我们调用orders中的getUser方法时候,开始进行延迟加载,去调用usermapper.xml中调用findUserById这个方法获取用户信息
延迟加载的配置
Mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中配置
lazyLoadingEnabled
aggressiveLazyLoading
注意上边两个单词的拼写,首字母都是小写的
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled"value="true"/>
<!-- 将积极加载改为消极加载 -->
<setting name="aggressiveLazyLoading"value="false"/>
</settings>
测试代码:
publicclass Demo9 {
//查询订单,关联查询用户信息,用户信息延迟加载
@Test
publicvoid findOrderUser() throwsException{
Stringresource="SqlMapConfig.xml";
InputStreamis=Resources.getResourceAsStream(resource);
SqlSessionFactorysqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
SqlSessionsqlsession=sqlSessionFactory.openSession();
OrdersMapperCustomerordersmappercustomer=sqlsession.getMapper(OrdersMapperCustomer.class);
List<Orders>list=ordersmappercustomer.findOrdersUserLazyLoading();
//遍历上边的订单列表
for(Orders orders :list){
//执行getUser()查询用户信息,实现延迟加载
Useruser=orders.getUser();
System.out.println(user);
}
sqlsession.close();
}
}
当执行了List<Orders> list=
ordersmappercustomer.findOrdersUserLazyLoading();后,只执行了select * from orders这条语句
当执行了getUser()的时候,才开始查询用户的信息。
当第二次循环查询改用户的时候,还是执行该语句。
上边的这个只发出了一条sql语句,而查找到了两个用户,因为一级缓存的原因,在后边学习。
延迟加载思考
不适用mybatis的association及其collection中的延迟加载的功能,如何实现延迟加载?
实现方法如下:
定义两个mapper方法:
1. 查询订单列表
2. 查询用户的信息
实现思路是先查询第一个mapper的方法,得到订单列表
在程序中(service层)中,按需去调用第二个mapper的方法查询用户信息。
总之:使用延迟加载方法,先去查询简单的SQL(最好单表,也可以关联查询),再去按需加载其他的信息。