Mybatis系列第9篇:延迟加载、鉴别器,java开发面试常问到的问题

private Double totalPrice;

}

测试用例

com.javacode2018.chat05.demo5.Demo5Test#getById1

@Test

public void getById1() throws IOException {

//指定mybatis全局配置文件

mybatisConfig = “demo5/mybatis-config.xml”;

this.before();

OrderModel orderModel = null;

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

orderModel = mapper.getById1(1);

}

log.info(“-------分割线--------”);

log.info(“{}”, orderModel.getUserModel());

}

运行输出

01:55.343 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

01:55.372 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)

01:55.431 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

01:55.431 [main] INFO  c.j.chat05.demo5.Demo5Test - -------分割线--------

01:55.432 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ==>  Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = ?

01:55.432 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ==> Parameters: 1(Integer)

01:55.435 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <==      Total: 2

01:55.439 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ==>  Preparing: SELECT id,name FROM t_user where id = ?

01:55.439 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ==> Parameters: 2(Integer)

01:55.441 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <==      Total: 1

01:55.441 [main] INFO  c.j.chat05.demo5.Demo5Test - UserModel(id=2, name=路人甲Java)

从日志中可以看出,总共有3次查询,后面2次查询在分割线之后出现的,说明是调用了orderModel.getUserModel()触发后面2次查询动作。

代码中我们调用的是获取用户信息,而订单列表信息也被加载了,这个主要是由于aggressiveLazyLoading被设置为true了,当使用到一个延迟加载的属性时,其他的延迟加载的属性也会被一起加载,所以触发了2个关联的查询。

下面我们看看将aggressiveLazyLoading设置为false的效果

再次运行测试用例输出

12:19.236 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

12:19.268 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)

12:19.336 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

12:19.337 [main] INFO  c.j.chat05.demo5.Demo5Test - -------分割线--------

12:19.338 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ==>  Preparing: SELECT id,name FROM t_user where id = ?

12:19.338 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ==> Parameters: 2(Integer)

12:19.340 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <==      Total: 1

12:19.341 [main] INFO  c.j.chat05.demo5.Demo5Test - UserModel(id=2, name=路人甲Java)

sqlmap中设置延迟加载

全局的方式会对所有的关联查询起效,影响范围比较大,mybatis也提供了在关联查询中进行设置的方式,只会对当前设置的关联查询起效。

关联查询,一般我们使用association、collection,这两个元素都有个属性fetchType,通过这个属性可以指定关联查询的加载方式。

fetchType值有2种,eager:立即加载;lazy:延迟加载。

下面我们来实现一个需求:还是通过订单id查询订单信息,并获取关联的用户信息、订单详细列表,用户信息我们要求立即加载,而订单详情我们要求延迟加载。

mapper xml如下
<![CDATA[ SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = #{value} ]]>

重点注意上面配置中association、collection这2个元素的fetchType属性,eager表示立即加载,lazy表示延迟加载。

测试用例

com.javacode2018.chat05.demo5.Demo5Test#getById2

@Test

public void getById2() throws IOException {

//指定mybatis全局配置文件

mybatisConfig = “demo5/mybatis-config2.xml”;

this.before();

OrderModel orderModel = null;

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

orderModel = mapper.getById2(1);

}

log.info(“-------分割线--------”);

log.info(“{}”, orderModel.getOrderDetailModelList());

}

运行输出

36:54.284 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

36:54.321 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)

36:54.385 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====>  Preparing: SELECT id,name FROM t_user where id = ?

36:54.385 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====> Parameters: 2(Integer)

36:54.387 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <====      Total: 1

36:54.389 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1

36:54.390 [main] INFO  c.j.chat05.demo5.Demo5Test - -------分割线--------

36:54.392 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ==>  Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = ?

36:54.392 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ==> Parameters: 1(Integer)

36:54.397 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <==      Total: 2

36:54.398 [main] INFO  c.j.chat05.demo5.Demo5Test - [OrderDetailModel(id=1, orderId=1, goodsId=1, num=2, totalPrice=17.76), OrderDetailModel(id=2, orderId=1, goodsId=1, num=1, totalPrice=16.66)]

注意输出中的分割线,可以分析得出,用户信息是和订单信息一起立即查出来的,而订单详情,是在我们调用orderModel.getOrderDetailModelList()获取订单列表的时候,采取懒加载的。

鉴别器(discriminator)

有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的), 鉴别器(discriminator)元素就是被设计来应对这种情况的,鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。

discriminator标签常用的两个属性如下:

  • column:该属性用于设置要进行鉴别比较值的列。

  • javaType:该属性用于指定列的类型,保证使用相同的java类型来比较值。

discriminator标签可以有1个或多个case标签,case标签有一个比较重要的属性:

  • value:该值为discriminator指定column用来匹配的值,当匹配的时候,结果会走这个case关联的映射。

我们使用鉴别器实现一个功能:通过订单id查询订单信息,当传入的订单id为1的时候,获取订单信息及下单人信息;当传入的订单id为2的时候,获取订单信息、下单人信息、订单明细信息;其他情况默认只查询订单信息。

OrderMapper.xml
<![CDATA[ SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = #{value} ]]>

注意上面的discriminator,这部分是关键,discriminator内部的case会和每行查询结果中的id字段进行匹配,匹配成功了case内部的关联查询会被执行,未匹配上的,只会走discriminator外部默认配置的映射映射规则。

UserMapper.xml
<![CDATA[ SELECT id,name FROM t_user where id = #{user_id} ]]>
OrderDetailMapper.xml
<![CDATA[ SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = #{order_id} ]]>
对应的3个Model类

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class OrderModel {

private Integer id;

private Integer userId;

private Long createTime;

private Long upTime;

//用户信息

private UserModel userModel;

//订单详情列表

private List orderDetailModelList;

}

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class UserModel {

private Integer id;

private String name;

}

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class OrderDetailModel {

private Integer id;

private Integer orderId;

private Integer goodsId;

private Integer num;

private Double totalPrice;

}

测试用例

com.javacode2018.chat05.demo6.Demo6Test#getById1

@Test

public void getById1() throws IOException {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

//查询订单为1的

OrderModel orderModel = mapper.getById1(1);

log.info(“{}”, orderModel);

log.info(“------------------------------------------------------------”);

//查询订单为2的

orderModel = mapper.getById1(2);

log.info(“{}”, orderModel);

log.info(“------------------------------------------------------------”);

//查询订单为3的

orderModel = mapper.getById1(3);

log.info(“{}”, orderModel);

}

}

运行输出

58:16.413 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

58:16.457 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)

58:16.481 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====>  Preparing: SELECT id,name FROM t_user where id = ?

58:16.481 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====> Parameters: 2(Integer)

58:16.488 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <====      Total: 1

58:16.489 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

58:16.489 [main] INFO  c.j.chat05.demo6.Demo6Test - OrderModel(id=1, userId=2, createTime=1578368161, upTime=1578368161, userModel=UserModel(id=2, name=路人甲Java), orderDetailModelList=null)

58:16.491 [main] INFO  c.j.chat05.demo6.Demo6Test - ------------------------------------------------------------

58:16.491 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

58:16.492 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 2(Integer)

58:16.493 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====>  Preparing: SELECT id,name FROM t_user where id = ?

58:16.493 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====> Parameters: 1(Integer)

58:16.494 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <====      Total: 1

58:16.495 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====>  Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = ?

58:16.495 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====> Parameters: 2(Integer)

58:16.505 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <====      Total: 1

58:16.505 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

58:16.506 [main] INFO  c.j.chat05.demo6.Demo6Test - OrderModel(id=2, userId=1, createTime=1578368161, upTime=1578368161, userModel=UserModel(id=1, name=张学友), orderDetailModelList=[OrderDetailModel(id=3, orderId=2, goodsId=1, num=1, totalPrice=8.88)])

58:16.506 [main] INFO  c.j.chat05.demo6.Demo6Test - ------------------------------------------------------------

58:16.506 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

58:16.506 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 3(Integer)

58:16.508 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

58:16.509 [main] INFO  c.j.chat05.demo6.Demo6Test - OrderModel(id=3, userId=1, createTime=1578368161, upTime=1578368161, userModel=null, orderDetailModelList=null)

输出中可以看出,订单1查询了2次,订单2查询了3次,订单3查询了1次;鉴别器算是一个不错的功能。

继承(extends)

继承在java是三大特性之一,可以起到重用代码的作用,而mybatis也有继承的功能,和java中的继承的作用类似,主要在resultMap中使用,可以重用其他resultMap中配置的映射关系。

用法

案例

下面我们使用继承来对上面的鉴别器的案例改造一下,优化一下代码

OrderMapper.xml

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip1024b 备注Java获取(资料价值较高,非无偿)
img

结语

小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。

我们选择的这个行业就一直要持续的学习,又很吃青春饭。

虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。

送给每一位想学习Java小伙伴,用来提升自己。

在这里插入图片描述

本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!
存中…(img-ZM9FMl4J-1711572059659)]
[外链图片转存中…(img-DOnKAoVN-1711572059660)]
[外链图片转存中…(img-h0999eJH-1711572059660)]
[外链图片转存中…(img-xxrln4Fk-1711572059661)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

[外链图片转存中…(img-87o77ud2-1711572059661)]
[外链图片转存中…(img-uY3ATylp-1711572059662)]

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip1024b 备注Java获取(资料价值较高,非无偿)
[外链图片转存中…(img-CR3HWDbN-1711572059662)]

结语

小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。

我们选择的这个行业就一直要持续的学习,又很吃青春饭。

虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。

送给每一位想学习Java小伙伴,用来提升自己。

[外链图片转存中…(img-T2q6IEub-1711572059663)]

本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值