推荐学习

  • 重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走
  • 全网独家的“MySQL高级知识”集合,骨灰级收藏,手慢则无
  • “吃”完这本Java性能调优实战,MySQL+JVM+Tomcat等问题一键全消

前言

之前分析了MyBatis中的配置的使用,而MyBatis中动态标签功能也非常强大,本文不会介绍全部标签,主要是针对resultMap来介绍复杂查询该如何利用sql标签来配置动态sql。

固定参数的查询

首先我们来看一个带有固定参数的查询语句该如何实现:
UserMapper.java中新增如下两个方法:

 List listUserByUserName(@Param("userName") String userName); List listUserByTable(@Param("tableName") String tableName);

对应UserMapper.xml中的sql语句为:

          select user_id,user_name from lw_user where user_name=#{userName}            select user_id,user_name from ${tableName}    

然后执行查询:

package com.lonelyWolf.mybatis;import com.alibaba.fastjson.JSONObject;import com.lonelyWolf.mybatis.mapper.UserMapper;import com.lonelyWolf.mybatis.model.LwUser;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;import java.util.List;public class MyBatisQueryByParam {    public static void main(String[] args) throws IOException {        String resource = "mybatis-config.xml";        //读取mybatis-config配置文件        InputStream inputStream = Resources.getResourceAsStream(resource);        //创建SqlSessionFactory对象        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        //创建SqlSession对象        SqlSession session = sqlSessionFactory.openSession();        /**         * 相比较于session.selectList("com.xxx.UserMapper.listAllUser")来实现查询,         * 下面这种通过先获取mapper再挑用mapper中方法的方式会更灵活         */        UserMapper userMapper = session.getMapper(UserMapper.class);        List userList = userMapper.listUserByUserName("孤狼1号");        System.out.println(null == userList ? "": JSONObject.toJSONString(userList));        List userList2 = userMapper.listUserByTable("lw_user");        System.out.println(null == userList2 ? "": JSONObject.toJSONString(userList2));    }}

查询结果输出如下:

#和$区别

从上面的输出sql语句截图可以看到,如果使用#的话,那么sql语句会先在sql语句中使用占位符,也就是预编译,对应JBDC中的PreparedStatement。而使用$,则会直接把参数拼到sql语句上,相当于JDBC中的Statement。

一般情况下不建议使用$,因为这种直接拼接的方式容易被sql注入攻击。
比如,上面的sql语句:

select user_id,user_name from ${tableName}

假如tableName传入的是:lw_user;delete from lw_user;那么这时候执行的sql语句就会变成:

select user_id,user_name from lw_user;delete from lw_user;

这时候整张表的数据都会被删除,而如果使用的是#{tableName},最终执行的是如下sql:

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看看会有什么结果:

可以看到外部查询有几条数据就会触发内部查询几次,这就是嵌套查询引发的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));//触发

输出结果为:

延迟加载原理

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

总结

本文主要讲述了如何利用MyBatis实现一对一,一对多以及多对多的查询,并且讲解了如何利用延迟加载来解决嵌套查询中的N+1问题。MyBatis系列中前两篇相对基础,并没有深入分析实现原理,仅仅只是讲解了如何使用,下一篇开始,将会深入分析MyBatis的源码以及一些高级特性比如sqlSession执行流程,缓存,参数和结果集映射等功能的实现原理。

作者:双子孤狼

原文链接:https://blog.csdn.net/zwx900102/article/details/108559220

mybatis一对多关联查询_一对一,一对多,多对多查询及延迟加载(N+1问题)分析相关推荐

  1. sql跨表查询_白话django之ORM的查询语句

    教程源码:z991/django_turital 在日常开发中,数据库的增删改查(CDUR)中,查询需求偏多,所以查询的语法比增删改操作多得多,尤其是跨表关联查询,可以让代码精简很多年. 直接上代码吧 ...

  2. 12333新农合网上查询_新农合医保查询缴费平台|新农村医疗保险网上缴费平台

    这个智慧新农合新农合患者就诊从挂号到缴费,不用再拿着各种票据在报销处和收费处之间奔走,也不用担心票据不完整造成无法正常报销的情况,只需携带本人有效身份证和农合本,凭身份证号码或新农合号码即可实时报销所 ...

  3. 模糊匹配查询_必须掌握的6个查询函数应用技巧,办公必备,收藏备用!

    在Excel中,有一类函数被称为查找和引用函数,今天,小编给大家分享6个查询函数(Choose.Lookup.Hlookup.Vlookup.Match.Index)的应用技巧,可以收藏备用哦! 一. ...

  4. sql 行政区划关联查询优化_最新IP数据库 存储优化 查询性能优化 每秒解析上千万...

    高性能IP数据库格式详解 每秒解析1000多万ip  qqzeng-ip-ultimate.dat 3.0版编码:UTF8字节序:Little-Endian 返回规范字段(如:亚洲|中国|香港|九龙| ...

  5. mysql 多表关联建模_(四)多对多模式 - 数据库模型设计专栏 - CSDN博客

    连载之5 原创:胖子刘(转载请注明出处及作者,谢谢.)(四)多对多模式 多对多模式,也是比较常见的一种数据库设计模式,它所描述的两个对象不分主次.地位对等.互为一对多的关系.对于A表来说,一条记录对应 ...

  6. postgres sql 多表联合查询_从零学会SQL-多表查询

    之前学习的内容几乎针对单个表进行简单操作,实际工作中可没有这么简单,复杂的表结构和多表数据关联进行分析,这时候需要我们掌握多表查询方法,一如既往,我们先看一下这篇的主要内容: 表的加法 表的联结 联结 ...

  7. 高性能mysql 小查询_高性能MySql进化论(十一):常见查询语句的优化

    总结一下常见查询语句的优化方式 1        COUNT 1.       COUNT的作用 ·        COUNT(table.filed)统计的该字段非空值的记录行数 ·         ...

  8. sql 上亿 查询_在文件上使用 SQL 查询的示例

    [摘要] 本文介绍直接用 SQL 查询文件数据的各种情况,并用 esProc SPL 举例实现.请点击在文件上使用 SQL 查询的示例了解详情 在数据分析业务中经常要处理数据文件.我们知道,对于数据库 ...

  9. mysql分组和where条件查询_【MySQL】:分组查询where和having

    分组查询 之前学习聚合函数,知道聚合函数在默认情况下,将会把所有的记录当成一组,让我们在对列求值,计算时更方便了一些. 但是,在某些情况下,我们需要显式地对记录进行分组,使用的是group by [c ...

最新文章

  1. Python匿名函数:lamdba()函数
  2. 一天学完spark的Scala基础语法教程二、运算符与分支语句(idea版本)
  3. BASH Shell 简易进度条小函数
  4. 同步异步 阻塞 非阻塞 异步调用 线程队列 协程
  5. 【推荐】 RAC 性能优化全攻略与经典案例剖析
  6. 开启灯光就是近光吗_科目三考前必看!夜间灯光语音模拟+超全项目操作
  7. 一张图学会python3语法-一张图理清 Python3 所有知识点
  8. 7-19 求链式线性表的倒数第K项
  9. Gambit学习2-曲面挖洞
  10. 微机原理笔记day01 计算机发展史,和计算机组成部分
  11. 直播场景音频降噪,传统算法 VS AI 算法对比和实践
  12. vmware下ubuntu从图形界面切换到控制台的快捷键
  13. 大数据CAP原则(CAP定理)、BASE理论
  14. SNK 推出NEOGEO mini 投币配件,还能当作存钱罐?
  15. 微信登录显示连接失败,请检查网络
  16. python深度学习include框架_《用Python实现深度学习框架》上市
  17. 【ZBH选讲·拍照】
  18. How to set the Default Page in ASP.NET?
  19. 阿里云发布全新开源操作系统『龙蜥』,支持 X86 64 和 ARM 64 架构及鲲鹏、飞腾等芯片...
  20. 【音视频处理】码率、帧率越高越清晰?分辨率、像素、dpi之间是什么关系?码率的真实作用,I帧、B帧、P帧是什么

热门文章

  1. ABAP/4 Open SQL
  2. NUMERIC_CHECK函数解析
  3. 东北全面放开生育,数据揭秘人口和GDP和房价间联系
  4. “红人经济第一股”搞虚拟社交,天下秀是变道还是扩道?
  5. 内存溢出_JVM|03内存溢出实战
  6. 台式计算机 启天 m4600,联想启天M4600-N000:高效的焕新之选——台式机
  7. 一个空值_java匠人手法优雅的处理空值
  8. python hashlib模块_python3 hashlib模块
  9. excel随机抽取_Excel条件格式,扮靓你的报表
  10. 【python教程】IO文件操作的常见用法