IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> MyBatis SQL执行过程与其他理论总结 -> 正文阅读

[大数据]MyBatis SQL执行过程与其他理论总结

目录

SQL执行过程

Xml 映射文件中,除了常见的 select|insert|update|delete 标签之外,还有哪些标签?

MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

MyBatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?

模糊查询like语句该怎么写?

MyBatis 一对一关联查询

MyBatis 一对多关联查询

传入sql参数 获取结果

为什么需要预编译

#{}和${}的区别是什么?

在mapper中如何传递多个参数?

MyBatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?

MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?

MyBatis 是否可以映射 Enum 枚举类?

如何获取自动生成的(主)键值?

MyBatis 执行批量插入,能返回数据库主键列表吗?

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

其他

简述 MyBatis 的插件运行原理,以及如何编写一个插件。

MyBatis 是如何进行分页的?分页插件的原理是什么?

Mybatis的一级、二级缓存


?注意:本文参考? ?MyBatis 常见面试总结 | JavaGuide

SQL执行过程

Xml 映射文件中,除了常见的 select|insert|update|delete 标签之外,还有哪些标签?

还有很多其他的标签,?<resultMap>?、?<parameterMap>?、?<sql>?、?<include>?、?<selectKey>?,加上动态 sql 的 9 个标签,?trim|where|set|foreach|if|choose|when|otherwise|bind?等,其中?<sql>?为 sql 片段标签,通过?<include>?标签引入 sql 片段,?<selectKey>?为不支持自增的主键生成策略标签。

MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

MyBatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能,MyBatis 提供了 9 种动态 sql 标签?

trim|where|set|foreach|if|choose|when|otherwise|bind?。

MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,通过 OGNL 语法来实现,但是如果要写的完整,必须配合 where,trim 节点,where 节点是判断包含节点有内容就插入 where,否则不插入,trim 节点是用来判断,如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or取掉。

使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。

原因就是 namespace+id 是作为?Map<String, MappedStatement>?的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。

MyBatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?

虽然 MyBatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,MyBatis 都可以正确识别。

原理是,MyBatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,MyBatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,MyBatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。

模糊查询like语句该怎么写?

第1种:在Java代码中添加sql通配符。

?第2种:在sql语句中拼接通配符,会引起sql注入

MyBatis 一对一关联查询

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配association节点配置一对一的类就可以完成;

嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

MyBatis 一对多关联查询

有联合查询和嵌套查询。

联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;

嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。

关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询,就可以把主对象和其关联对象查出来。

那么问题来了,join 查询出来 100 条记录,如何确定主对象是 5 个,而不是 100 个?其去重复的原理是?<resultMap>?标签内的?<id>?子标签,指定了唯一确定一条记录的 id 列,MyBatis 根据?<id>?列值来完成 100 条记录的去重复功能,?<id>?可以有多个,代表了联合主键的语意。

同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。

举例:下面 join 查询出来 6 条记录,一、二列是 Teacher 对象列,第三列为 Student 对象列,MyBatis 去重复处理后,结果为 1 个老师 6 个学生,而不是 6 个老师 6 个学生。

t_idt_names_id
1teacher38
1teacher39
1teacher40
1teacher41
1teacher42
1teacher43

传入sql参数 获取结果

为什么需要预编译

SQL 预编译指的是数据库驱动在发送 SQL 语句和参数给 DBMS 之前对 SQL 语句进行编译,这样
DBMS 执行 SQL 时,就不需要重新编译。

JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化SQL 的执行。预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的
SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以
重复利用。把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个
SQL,可以直接使用这个缓存的 PreparedState 对象。Mybatis默认情况下,将对所有的 SQL 进行
预编译。

还有一个重要的原因,防止SQL注入

#{}和${}的区别是什么?

${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc. Driver它属于字符串替换。

#{}是 sql 的参数占位符,MyBatis 会将 sql 中的#{}替换为? 号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的? 号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name}?的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于?param.getItem().getName()它属于预编译处理。

在mapper中如何传递多个参数?

方法1:顺序传参法

#{}里面的数字代表传入参数的顺序。

这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。

方法2:@Param注解传参法?

#{}里面的名称对应的是注解@Param括号里面修饰的名称。

这种方法在参数不多的情况还是比较直观的,(推荐使用)。

方法3:Map传参法?

#{}里面的名称对应的是Map里面的key名称。

这种方法适合传递多个参数,且参数易变能灵活传递的情况。(推荐使用)。

方法4:Java Bean传参法?

#{}里面的名称对应的是User类里面的成员属性。

这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方
便,推荐使用。(推荐使用)。?

MyBatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用?<resultMap>?标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用 resultType,和sql 列的别名功能,将列别名书写为对象属性名

比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,MyBatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,MyBatis 一样可以正常工作。

有了列名与属性名的映射关系后,MyBatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。?

他们两个的区别就是,sql返回结果字段a,b。

resultType就是直接找到某个java的pojo M,利用反射将M的a字段设置成sql结果字段a的值。

resultMap就是有一个额外的映射,结果字段a,b映射成A,B,再把sql映射结果映射到某个java的pojo M,利用反射将M的a字段设置成sql结果字段A,B的值。

MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?

MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载?lazyLoadingEnabled=true|false。

它的原理是,使用?CGLIB?创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用?a.getB().getName()?,拦截器?invoke()?方法发现?a.getB()?是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成?a.getB().getName()?方法的调用。这就是延迟加载的基本原理。

当然了,不光是 MyBatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。

MyBatis 是否可以映射 Enum 枚举类?

MyBatis 可以映射枚举类,不单可以映射枚举类,MyBatis 可以映射任何对象到表的一列上。映射方式为自定义一个?TypeHandler?,实现?TypeHandler?的?setParameter()?和?getResult()?接口方法。?TypeHandler?有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为?setParameter()?和?getResult()?两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。

如何获取自动生成的(主)键值?

insert方法总是返回一个int值,这个值代表的是插入的行数。如果采用自增长策略,自动生成的键值在insert方法执行完后可以被设置到传入的参数对象中。示例:?

MyBatis 执行批量插入,能返回数据库主键列表吗?

能,JDBC 都能,MyBatis 当然也能。

当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

其他

简述 MyBatis 的插件运行原理,以及如何编写一个插件。

MyBatis 仅可以编写针对?ParameterHandler、?ResultSetHandler、?StatementHandler、?Executor?这 4 种接口的插件

MyBatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是?InvocationHandler?的?invoke()?方法,当然,只会拦截那些你指定需要拦截的方法。

实现 MyBatis 的 Interceptor 接口并复写?intercept()?方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

MyBatis 是如何进行分页的?分页插件的原理是什么?

1?MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;

2 可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能

3 也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

举例:?select _ from student?,拦截 sql 后重写为:?select t._ from (select \* from student)t limit 0,10

Mybatis的一级、二级缓存

一级缓存

基于PerpetualCache 的HashMap本地缓存,其存储作用域为Session,当Sessionflush 或 close之后,该Session 中的所有Cache就将清空,默认打开一级缓存。

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。

每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。具体实现类的类关系图如下图所示:

1. MyBatis一级缓存的生命周期和 SqlSession一致。

2. MyBatis 一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。

3. MyBatis 的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。?

二级缓存

在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。

二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程为:

二级缓存->一级缓存->数据库

1 MyBatis 的二级缓存相对于一级缓存来说,实现了SqlSession 之间缓存数据的共享,同时粒度更加细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。

2 MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。

3 在分布式环境下,由于默认的MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis, Memcached 等分布式缓存可能成本更低,安全性也更高。?

4 二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache, HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> ;

5 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear.

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:36:32  更:2022-02-28 15:39:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:29:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码