一对一,一对多,多对多查询及延迟加载(N+1问题)分析,简直无敌

select user_id,user_name from ‘lw_user;delete from lw_user;’

产生的后果只是查询了一张不存在的表而已。

动态参数的查询

上面的例子中参数是固定的,那么假如我们参数不固定呢?比如有2个参数,但是我可能一个都不用,也可能只用1个,或者2个都用。这种又该如何实现呢? 如下图所示,可以通过where和if标签结合使用,两个条件都写了and,这是因为Mybatis会帮我们处理掉多余的and关键字。

select user_id,user_name from lw_user and user_id=#{userId} and user_name=#{userName}

或者说我们对同一个参数需要进行不同取值拼接不同的sql,那么可以通过choose标签根据不同的参数拼接不同的sql

select user_id,user_name from lw_user



and user_id=#{userId}


and user_id=#{userId}


and user_id=#{userId}



当然,Mybatis还提供了其他许多标签,用来处理更加复杂的组合,在这里就不举例说明了。

一对一查询

假如我们现在有两种表,是一对一关系,我们想同时查询出来,当然最简单的办法是再写一个类,把两张表的结果属性都放到一个类里面,但是这种方式无疑会造成了很多重复代码,而且体现不出层级关系,假如我们有一张表lw_user表,存储用户信息,另一张表lw_user_job存储了用户的工作经历,那么很明显,job对应类应该包含在user类内,这种应该怎么实现呢?

请看!

1、新建一个实体类UserJob来映射lw_user_job表属性:

package com.lonelyWolf.mybatis.model;

public class LwUserJob {
private String id;
private String userId; //用户id
private String companyName; //公司名
private String position; //职位

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getCompanyName() {
return companyName;
}

public void setCompanyName(String companyName) {
this.companyName = companyName;
}

public String getPosition() {
return position;
}

public void setPosition(String position) {
this.position = position;
}
}

2、在原先的LwUser类增加一个引用属性来引用LwUserJob:

package com.lonelyWolf.mybatis.model;

public class LwUser {
private String userId; //用户id
private String userName; //用户名称

private LwUserJob usreJobInfo;//用户工作信息

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public LwUserJob getUsreJobInfo() {
return usreJobInfo;
}

public void setUsreJobInfo(LwUserJob usreJobInfo) {
this.usreJobInfo = usreJobInfo;
}
}

3、UserMapper.java中新增一个方法:

List listUserAndJob();

这时候UserMapper.xml需要自定义一个ResultMap:

select * from lw_user u inner join lw_user_job j on u.user_id=j.user_id

这时候执行查询就可以得到如下结果:

[{“userId”:“1”,“userJobInfo”:{“companyName”:“自由职业”,“id”:“11”,“position”:“初级开发”},“userName”:“孤狼1号”}]

一对多查询

假设用户信息和工作信息是1对多的关系,又该如何呢? 只需做2步简单的改造: 1、将LwUser中引用属性改为List:

private List userJobList;

2、Mapper中的ResultMap文件同时做出修改,通过collection标签代替association标签,同时javaType修改为ofType:

再次执行查询得到如下结果:

[{“userId”:“1”,“userJobList”:[{“companyName”:“自由职业”,“id”:“11”,“position”:“初级开发”}],“userName”:“孤狼1号”}]

可以看到这时候的userJobList已经是一个数组了。

PS:记得之前有人问过属性的映射是不是必须把表里面所有的属性都映射才行,答案是否定的,需要几个就映射几个,不需要完全映射过来。

多对多查询

多对多其实和一对多差不多的原理,都是利用collection标签,就是在collection标签里面再嵌套collection标签就可以实现多对多的查询,在这里就不在举例了。

延迟加载(解决N+1问题)

我们先来看一下一对多的另一种写法,就是支持一种嵌套查询:

select * from lw_user select * from lw_user_job where user_id=#{userId}

上面的collection内部并没有定义属性,但是collection上面定义了两个标签,代表的含义是将当前查询结果的值user_id传递到查询selectJob中去。我们定义方法来执行一下这个外部查询selectUserAndJob看看会有什么结果:

image

可以看到外部查询有几条数据就会触发内部查询几次,这就是嵌套查询引发的N+1问题。(使用association标签也会存在这个问题)

这种在对性能要求比较高的场景中是不允许的,非常的浪费资源,MyBatis官方也不建议我们使用这种方式。

解决N+1问题

MyBatis虽然不建议我们使用这种嵌套查询,但是也提供了一种解决N+1问题的方式,那就是当我们执行外部查询的时候不会触发内部查询,仅仅当我们使用到了内部对象的时候才会触发内部查询来获取对应结果,这种就叫延迟加载。

延迟加载需要通过全局属性来控制,默认是关闭的。 我们在mybatis-config.xml文件中开启延迟加载试试:

然后我们再来执行如下语句:

List userList = userMapper.selectUserAndJob();
System.out.println(userList.size());//不会触发
System.out.println(userList.get(0).getUserJobList());//触发
System.out.println(null == userList ? “”: JSONObject.toJSONString(userList));//触发

输出结果为:

![image](https://upload-images.jianshu.io/upload_images/24613101-8d7928e61023ff36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

延迟加载原理

延迟加载其实就是利用了动态代理来生成一个新的对象,默认用的是Javassist动态代理,可以通过参数来控制,支持切换为CGLIB:
oad_images/24613101-8d7928e61023ff36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

延迟加载原理

延迟加载其实就是利用了动态代理来生成一个新的对象,默认用的是Javassist动态代理,可以通过参数来控制,支持切换为CGLIB:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值