春招准备之MyBatis框架篇

本系列内容直接以八股文,即问题的形式总结,面试所需内容

1、什么是MyBatis?

MyBatis 是一个半自动的持久层框架,它不屏蔽 SQL,而是让开发者100%掌控 SQL,同时自动化处理 "参数映射 + 结果集映射 + 资源管理" 等重复性工作

(1)半 ORM 框架(核心本质)

半ORM 的含义是:

不是完全自动:需要手写 SQL(优点:性能可控、SQL 调优灵活)

不是纯 JDBC:自动处理 Connection、PreparedStatement、ResultSet 的创建和关闭

"半"的优势 :结合了 JDBC 的灵活性 和 ORM 的便捷性,避免了 Hibernate 全自动化带来的性能黑箱和 SQL 不可控问题

(2)两种配置方式(XML vs 注解)

XML:<select id="findUser" resultType="User">SELECT * FROM user WHERE id = #{id}</select>

注解:@Select("SELECT * FROM user WHERE id = #{id}")

重要原则 复杂 SQL 一定要用 XML ,注解的可读性和维护性极差

(3)执行流程

// UserMapper.java
User user = userMapper.findUser(1);

**// MyBatis 背后自动完成:

  1. 加载 UserMapper.xml 中的 <select id="findUser">...
  2. 创建 PreparedStatement,将 #{id} 替换为 ?,并设置参数 1
  3. 执行 SQL,获取 ResultSet
  4. 自动映射:rs.getInt("id") → user.setId(),rs.getString("name") → user.setName()
  5. 关闭连接,返回 User 对象**

关键自动点 : 参数映射 (#{id} → ?)和 结果映射 **(列名 → 属性名)是 MyBatis 的核心价值

MyBatis = SQL 写手的最爱 + JDBC 的终结者 + 半自动 ORM 的标杆

2、说说MyBatis的优点和缺点?

优点:

(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重

(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连 接

(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)

(4)能够与Spring很好的集成

(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象 关系组件维护

缺点:

(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求

(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

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

#{} 是**"占位符"** :SQL 预编译,防注入

${} 是**"字符串拼接"** :直接替换,有注入风险

#{值} 防注入,99% 用它;${名} 只用于表名/列名/排序,绝不碰用户输入!

#{} 是预编译参数占位符,能自动防 SQL 注入,适用于所有参数值传递;

{} 是字符串直接替换,存在 SQL 注入风险,仅限用于动态表名、列名或排序字段等无法预编译的场景。 因此,实际开发中应 99% 使用 #{},仅在确保安全且必要时才使用 {},绝不可将其用于用户输入的参数赋值

(#{值} 防注入,99% 用它;${名} 只用于表名/列名/排序,绝不碰用户输入!)

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

第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致

第2种: 通过来映射字段名和实体类属性名的一一对应的关系

用id属性来映射主键字段

用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性

5、Mybatis是如何进行分页的?分页插件的原理是什么?

1、RowBounds分页:基于ResultSet的内存分页,非物理分页,数据量大时性能差

2、物理分页实现方式

  • 手动在SQL中拼接分页参数(如MySQL的LIMIT)

  • 使用分页插件:通过实现MyBatis插件接口,在拦截方法中改写SQL,根据方言添加物理分页语句和参数

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

MyBatis 通过反射将 SQL 结果封装为目标对象

映射形式有两种:

  1. 使用 resultMap 标签,逐一定义列名与属性名的映射关系

  2. 使用 SQL 别名使列名直接匹配属性名

建立映射后,MyBatis 通过反射创建对象并赋值,未映射的属性无法赋值

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

除基本 CRUD 标签外,MyBatis XML 映射文件还包括:

动态 SQL 标签(9个):if, choose, when, otherwise, trim, where, set, foreach, bind

辅助标签:

<sql>:定义可复用的 SQL 片段

<include>:引入 SQL 片段,可配合<property> 传递参数

<selectKey>:获取主键值(适用于不支持自增的数据库)

<resultMap>:自定义结果集映射 <cache> / <cache-ref>:二级缓存配置与引用

<parameterMap>:参数映射(已废弃)

9、MyBatis实现一对一有几种方式?具体怎么操作的?

联合查询:是几个表联合查询,通过 JOIN 只执行一次 SQL,在 resultMap 的 association 节点中直接配置一对一类的映射关系

嵌套查询:先查主表,再根据主表结果的外键 id,通过 association 的 select 属性指定从表查询语句,分两次查询

10**、Mybatis是否支持延迟加载?它的实现原理是什****么?**

MyBatis 支持 association (一对一)和 collection(一对多)对象的延迟加载

配置 :在 MyBatis 配置文件中设置 lazyLoadingEnabled=true 开启。

原理 :使用 CGLIB 创建目标对象代理,当调用目标方法(如 a.getB().getName())时,拦截器 invoke() 发现 a.getB() 为 null,则单独执行预存的 SQL 查询关联对象 B,调用 a.setB(b) 赋值后再完成方法调用。该原理同样适用于 Hibernate 等框架

不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的

11**、说说Mybatis的缓存机制?**

整体架构:MyBatis 提供两级缓存,一级缓存基于 SqlSession,二级缓存基于 namespace 全局共享

一级缓存(LocalCache):

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的 SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的 SQL 语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。每个 SqlSession 中持有了 Executor,每个 Executor 中有一个 LocalCache。当用户发起查询时, MyBatis 根据当前执行的语句生成MappedStatement,在 Local Cache 进行查询,如果缓存命中 的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache,最后返回结果给用户

生命周期:一级缓存的生命周期和 SqlSession 一致,会话结束即清空

实现机制:Executor 内置 HashMap 缓存,查询时优先从缓存获取,未命中则查询数据库并写入缓存

局限性:无容量限制,功能简单;多 SqlSession 或分布式环境下,写操作会导致脏数据,建议设置为 Statement 级别

二级缓存:

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

实现机制:通过 CachingExecutor 装饰 Executor,在二级缓存未命中时才查询一级缓存和数据库

特点:同一 namespace 下所有 SqlSession 共享,粒度更细,支持 Cache 接口组合,可控性更强

缺陷:多表查询易产生脏数据,使用条件苛刻;分布式环境下默认本地实现会读取脏数据

当开启缓存后,数据的查询执行的流程为: 二级缓存 -> 一级缓存 -> 数据库

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

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

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

12**、JDBC编程有哪些步骤?**

1、装载相应的数据库的 JDBC 驱动并进行初始化:

Class.forName("com.mysql.jdbc.Driver");

2、建立 JDBC 和数据库之间的 Connection 连接:
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?
characterEncoding=UTF-8", "root", "123456");

3、创建 Statement 或者 PreparedStatement 接口,执行 SQL 语句

4、处理和显示结果

5、释放资源

13**、MyBatis中见过什么设计模式?**

**建造者模式:**SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder

**单例模式:**ErrorContext、LogFactory

**代理模式:**MapperProxy、ConnectionLogger(JDK 动态代理),executor.loader 包(CGLIB/javassist 延迟加载)

**组合模式:**SqlNode 及其子类(如 ChooseSqlNode)

**模板方法模式:**BaseExecutor、SimpleExecutor、BaseTypeHandler

**适配器模式:**Log 接口及对接 JDBC 日志的适配

**装饰者模式:**cache.decorators 子包中各类缓存装饰器

**迭代器模式:**PropertyTokenizer

**工厂模式:**SqlSessionFactory、ObjectFactory、MapperProxyFactory

14**、MyBatis中比如UserMapper.java是接口,为什么没有实现类还能调用?**

MyBatis 通过 JDK 动态代理 为 Mapper 接口生成代理对象,调用接口方法时实际执行 MapperProxy.invoke(),该方法解析方法签名,定位对应 SQL 并执行,最终返回结果

相关推荐
你不是我我2 小时前
【Java 开发日记】我们来说一下 Mybatis 的缓存机制
java·spring·mybatis
Helibo443 小时前
C++pair学习笔记
c++·笔记·学习
CarmenHu3 小时前
大模型应用评估指标学习笔记
笔记·学习
optimistic_chen3 小时前
【Java EE进阶 --- SpringBoot】Spring 核心 --- AOP
spring boot·笔记·spring·java-ee·aop·java注解
卡提西亚4 小时前
一本通网站1130:找第一个只出现一次的字符
数据结构·c++·笔记·算法·一本通
蒙奇D索大4 小时前
【算法】递归算法的深度实践:深度优先搜索(DFS)从原理到LeetCode实战
c语言·笔记·学习·算法·leetcode·深度优先
小松の博客5 小时前
Mybatis 注解开发
java·tomcat·mybatis
杰克尼5 小时前
Springcloud_day01
spring boot·spring·mybatis
不平衡的叉叉树6 小时前
Mybatis常用动态SQL标签
java·mybatis