不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:php-note.com  发布于:2022-05-15 16:29  分类:Java框架/系统  编辑

一、前言

在前面的博客中,小编向大家介绍了级联查询,针对不同的类型(一对一,一对多)进行了不同的选择:

一对一的时候,我们可以选择resultType或resultMap,resultType要有一个和查询出来的字段对应的Model,而resultMap需要我们写一个resultMap,使用association连接两个表。

一对多的时候,我们使用resultMap比较方便,使用collection来连接两个表。

当然,这里就有了一个问题,我们可不可以在用到的时候,再去查呢,做一个延迟加载?

二、延迟加载

延迟加载(Lazy load)是Hibernate3关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓的延迟加载就是当在真正需要的时候,才真正执行数据加载。

可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。

三、Mybatis延迟加载实现

resultMap的两种方式级联查询方式,association和collection具有延迟加载的功能,他们的实现反射是一样的,这里小编就以association举例实践一下。

3.1 步骤

  1. 配置开启延迟加载
  2. 查询主干sql,主要是单表的,也可以是关联查询的。
  3. 按照需要加载关联查询的信息

3.2 实现

需求:一个订单对应一个用户,查询订单的时候顺便把用户信息查出来

配置开启延迟加载:在mybatis的配置文件SqlMapConfig.xml中setting配置

<settings>
	<setting name="lazyLoadingEnabled" value="true"/>
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

参数说明:

  • lazyLoadingEnable :全局设置是否开启懒加载,默认为false。
  • aggressiveLazyLoading:侵略性 lazy loading 开关, 默认为true。这个属性比较搞笑,如果为true则当你访问任何一个属性都会加载所有的其他lazy load属性,即使你根本没有调用那个lazy load属性,说白了就是aggressiveLazyLoading=true,则lazy load等于没用,所以要使用lazy load还是将其设为false。

在上一篇博客中,小编用resultType和resultMap同上实现了这个功能,下面小编就对resultMap进行修改 ,实现延迟加载。

OrdersMapper.xml:

变化说明:这里订单和用户是一对一的,所以使用了assocation。这里面的一些属性,在前面介绍过了。这里重点介绍一下,懒加载的属性。

<!-- 延迟加载的resultMap -->
<resultMap id="OrderMap" type="com.dmsd.pojo.Orders">
	<!--对订单信息进行映射配置  -->
    <id column="id" property="id"/>
    <result column="user_id" property="userId"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- 实现对用户信息进行延迟加载 --> 
    <!-- 实现对用户信息进行延迟加载
            select:指定延迟加载需要执行的statement的id(是根据User的主键查询用户信息的statement)
            要使用userMapper.xml中selectByPrimaryKey完成根据用户id(user_id)用户信息的查询,如果findUserById不在本mapper中需要前边加namespace
            column:订单信息中关联用户信息查询的列,是user_id
             -->

    <association property="user" javaType="com.dmsd.pojo.User" select="com.dmsd.dao.UserMapper.selectByPrimaryKey" column="user_id">
    </association>

</resultMap>

<select id="queryOrderMap" resultMap="OrderMap">
    SELECT * FROM orders
</select>

运行测试:

@Test
public void testqueryOrderMap(){
	List<Orders> ordersList = orderService.queryOrderMap();

	System.out.println("------------------------------------------------------");

	System.out.println(ordersList);
}

 以debug方式运行:

当运行到 System.out.println("------------------------------------------------------");的时候,在控制台输出的sql语句只有SELECT * FROM orders;这个时候,我们只是做了查询,没有真正用到其中的数据。

继续运行后,System.out.println(ordersList);会真正的使用其中的数据,所以就进行了查询:

这就是延迟加载,在用的时候,才会去查。

四、使用懒加载可能遇到的问题

4.1 Cannot enable lazy loading because CGLIB is nott available .

原因:少cglib.jar,添加就可以了。

<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>2.2.2</version>
</dependency>

五、resultType、resultMap、延迟加载使用场景总结      

延迟加载:

延迟加载实现的方法多种多样,在只查询单表就可以满足需求,为了提高数据库查询性能使用延迟加载,再查询关联信息。

注意:mybatis提供的延迟加载的功能用于Service。

resultType:

作用:将查询结果按照sql列名和pojo属性名的一致性映射到pojo中。

场合:常见一些明细记录的展示,将关联信息全部展示到页面上时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo即可)

resultMap:

使用association和collection完成一对一和一对多高级映射。

association:

作用:将关联查询信息映射到一个pojo类中。

场合:为了方便获取关联信息可以使用association将关联订单映射为pojo,比如:查询订单关联查询用户信息。

collection:

作用:将关联查询信息映射到一个list集合中。

场合:为了方便获取关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块和功能,可以使用collection将模块和功能列表映射到list中。

 

摘自:https://blog.csdn.net/kisscatforever/article/details/79485851

 

 




MyBatis延迟加载 示例

延迟加载:假设存在用户、订单两张表,可以查询用户(User)及用户对应的订单(Order)列表(一对多);用户信息作为主体,而订单信息不是立即需要获取到的情况下,MyBatis提供延迟加载的策略,发送SQL执行语句时,只查询用户信息,当需要使用到订单信息时,即user.getOrderList()时,才会发送获取订单信息的SQL查询订单信息 (需要用到对应的信息时,才执行相关SQL);

MyBatis延迟加载本质上:通过动态代理的形式,创建了目标对象(User)的代理对象,拦截了对象的getting方法,在执行getting方法时,进入拦截器的invoke方法,当发现需要延迟加载时,会把之前存放好的SQL语句进行执行,并调用对象(User)调用set方法存值,然后调用对象本身的get方法取值

1、延迟加载开启(局部)

(1). MyBatis中延迟加载的实现是,将复杂的SQL语句进行拆分分步执行从而到达延迟的目的

如下根据订单ID查询订单及对应用户 (建议:一对多,多对多通常采用延迟加载,一对一通常采用立即加载,此案例仅为了简单说明)的多表关联查询,分为两步

select * from orders o left join user u on o.uid = u.id where  o.id = 1 

根据i订单ID查询用户 

根据第一步查出来的订单对应的uid(即两表关联时的订单表的UID)作为ID查询用户表

SELECT * FROM orders O where id = 1
 
SELECT * FROM user where id = #{uid}

(2). 增加两个对应的接口及sql编写

<select id="findById" resultMap="orderUserResult" parameterType="int">
	SELECT * FROM orders O where id = #{id}
</select>


<select id="findById" parameterType="int" resultType="com.kay.pojo.User">
	select * from user where id = #{id}
</select>

(3). 在orderMapper.xml中,在定义Order返回的结果集类型中<association>标签中 增加三个属性 fetchType="lazy" select="com.kay.dao.UserMapper.findById" column="uid"

  1. fetchType:可以设置为lazy(懒加载)、eager(立即加载 默认此加载模式);
  2. select: 为order对象中涉及到的根据订单ID获取user对象对应的接口地址;
  3. colunm:为查询用户信息时,order表中需要传入的字段;( 属性可配置在<association> 或者 <collection>标签下)
<resultMap id="orderUserResult" type="com.kay.pojo.Order">
	<result column="id" property="id"></result>
	<result column="ordertime" property="orderTime"></result>
	<result column="total" property="total"></result>

	<!-- 局部延迟加载 2  !!!!!!!! -->
	<!-- fetchType="lazy"  默认为eager  select 拆分成多条SQL语句后,在 SELECT * FROM orders O where id = #{id} 后执行的另一条对应的mapper类地址
	column="uid" 当前order 表是uid与user表中字段关联 -->
	<association property="user" javaType="com.kay.pojo.User" fetchType="lazy"
				 select="com.kay.dao.UserMapper.findById" column="uid">
		<result column="uid" property="id"></result>
		<result column="username" property="username"></result>
		<result column="password" property="password"></result>
		<result column="birthday" property="birthday"></result>
	</association>
</resultMap>

(4). 验证 查询order及调用Order中getOrderTime不会执行查询用户信息的SQL,只有调用getUser后才发送了查询用户信息的SQL

2、延迟加载开启(全局)  当全局和局部同时配置了,以局部的为准

在核心配置文件中增加<setting> 配置 lazyLoadingEnabled为true

<settings>
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

3、建议

一对多,多对多通常采用延迟加载,一对一通常采用立即加载

 

 

摘自:https://blog.csdn.net/sunshineKay/article/details/118398890