文章目录
- [1. 介绍下MyBatis?](#1. 介绍下MyBatis?)
- [2. MyBatis 框架的应用场景?](#2. MyBatis 框架的应用场景?)
- [3. MyBatis 有哪些优点?](#3. MyBatis 有哪些优点?)
- [4. MyBatis 有哪些缺点?](#4. MyBatis 有哪些缺点?)
- [5. MyBatis 用到了哪些设计模式?](#5. MyBatis 用到了哪些设计模式?)
- [6. MyBatis常用注解有哪些?](#6. MyBatis常用注解有哪些?)
- [7. MyBatis 有哪些核心组件?](#7. MyBatis 有哪些核心组件?)
- [8. MyBatis编程步骤是什么样的?](#8. MyBatis编程步骤是什么样的?)
- [9. MyBatis 和JDBC有什么区别?](#9. MyBatis 和JDBC有什么区别?)
- [10. MyBatis 中的缓存机制有啥用?](#10. MyBatis 中的缓存机制有啥用?)
- [11. MyBatis 一级缓存和二级缓存的区别?](#11. MyBatis 一级缓存和二级缓存的区别?)
- [12. MyBatis 一级缓存和二级缓存是什么数据结构?](#12. MyBatis 一级缓存和二级缓存是什么数据结构?)
- [13. MyBatis 中的缓存有哪些实现类型?](#13. MyBatis 中的缓存有哪些实现类型?)
- [14. MyBatis 默认会开启缓存机制吗? 怎么开启?](#14. MyBatis 默认会开启缓存机制吗? 怎么开启?)
- [15. MyBatis 为什么默认不会开启二级缓存?](#15. MyBatis 为什么默认不会开启二级缓存?)
- [16. MyBatis 中的缓存什么时候会被清理?](#16. MyBatis 中的缓存什么时候会被清理?)
- [17. MyBatis 二级缓存清理策略有哪些?](#17. MyBatis 二级缓存清理策略有哪些?)
- [18. MyBatis 接口绑定有哪几种方式?](#18. MyBatis 接口绑定有哪几种方式?)
- [19. MyBatis 有哪几种 SQL 编写形式?](#19. MyBatis 有哪几种 SQL 编写形式?)
- [20. MyBatis 映射文件中有哪些顶级元素?](#20. MyBatis 映射文件中有哪些顶级元素?)
- [21. MyBatis 映射时 A 引用了 B,如果 B 在 A 后面会怎样?](#21. MyBatis 映射时 A 引用了 B,如果 B 在 A 后面会怎样?)
- [22. MyBatis 中 Mapper 接口的实现原理是?](#22. MyBatis 中 Mapper 接口的实现原理是?)
- [23. MyBatis用注解绑定和用XML文件绑定有什么区别?](#23. MyBatis用注解绑定和用XML文件绑定有什么区别?)
- [24. MyBatis通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重载?](#24. MyBatis通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重载?)
- [25. MyBatis 中 Mapper 中的 SQL 语句可以重载吗?](#25. MyBatis 中 Mapper 中的 SQL 语句可以重载吗?)
- [26. MyBatis动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?](#26. MyBatis动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?)
- [27. MyBatis实体类中的属性名和表中的字段名不一样 ,怎么办?](#27. MyBatis实体类中的属性名和表中的字段名不一样 ,怎么办?)
- [28. MyBatis 配置文件中的 SQL id 是否能重复?](#28. MyBatis 配置文件中的 SQL id 是否能重复?)
- [29. MyBatis 支持哪些传参数的方法?](#29. MyBatis 支持哪些传参数的方法?)
- [30. MyBatis 的$和# 传参的区别?](# 传参的区别?)
- [31. MyBatis 可以映射到枚举类吗?](#31. MyBatis 可以映射到枚举类吗?)
- [32. MyBatis 怎么封装动态 SQL?](#32. MyBatis 怎么封装动态 SQL?)
- [33. MyBatis trim 标签有什么用?](#33. MyBatis trim 标签有什么用?)
- [34. MyBatis where 标签有什么用?](#34. MyBatis where 标签有什么用?)
- [35. MyBatis 是如何进行分页的?分页插件的原理是什么?](#35. MyBatis 是如何进行分页的?分页插件的原理是什么?)
- [36. MyBatis 有几种分页方式?](#36. MyBatis 有几种分页方式?)
- [37. MyBatis 逻辑分页和物理分页的区别是什么?](#37. MyBatis 逻辑分页和物理分页的区别是什么?)
- [38. MyBatis 流式查询有什么用?](#38. MyBatis 流式查询有什么用?)
- [39. MyBatis 模糊查询 like 语该怎么写?](#39. MyBatis 模糊查询 like 语该怎么写?)
- [40. MyBatis 如何防止 SQL 注入?](#40. MyBatis 如何防止 SQL 注入?)
- [41. MyBatis 如何获取自动生成的主键id?](#41. MyBatis 如何获取自动生成的主键id?)
- [42. MyBatis 中`jdbcType` 和`javaType` 的区别?](#42. MyBatis 中
jdbcType
和javaType
的区别?) - [43. 什么时候必须指定`jdbcType` 和`javaType`?](#43. 什么时候必须指定
jdbcType
和javaType
?) - [44. MyBatis 支持预编译吗? 怎么做?](#44. MyBatis 支持预编译吗? 怎么做?)
- [45. MyBatis 中的事务管理方式?](#45. MyBatis 中的事务管理方式?)
- [46. 怎么开启事务?](#46. 怎么开启事务?)
- [47. MyBatis 事务和 Spring 事务有什么区别?](#47. MyBatis 事务和 Spring 事务有什么区别?)
- [48. MyBatis 使用了哪些设计模式?](#48. MyBatis 使用了哪些设计模式?)
- [49. 什么是 JPA?](#49. 什么是 JPA?)
- [50. MyBatis 中 `StatementHandler` 和 `MappedStatement` 区别?](#50. MyBatis 中
StatementHandler
和MappedStatement
区别?) - [51. MyBatis 常用的 `TypeHandler` 有哪些?](#51. MyBatis 常用的
TypeHandler
有哪些?) - [52. MyBatis 执行流程是怎样的?](#52. MyBatis 执行流程是怎样的?)
- [53. MyBatis 中的 `SglSession` 是线程安全的吗?](#53. MyBatis 中的
SglSession
是线程安全的吗?) - [54. MyBatis 中的 `SglSession` 有哪些实现类?](#54. MyBatis 中的
SglSession
有哪些实现类?) - [55. MyBatis 中的 `DefaultSqlSession` 为什么不是线程安全的?](#55. MyBatis 中的
DefaultSqlSession
为什么不是线程安全的?) - [56. MyBatis 中 `SqlSessionTemplate` 与 `SqlSessionManager` 的区别?](#56. MyBatis 中
SqlSessionTemplate
与SqlSessionManager
的区别?)
1. 介绍下MyBatis?
MyBatis 是一个帮你简化数据库操作的工具,特别适合 Java 程序员。它的主要作用就是让你不用自己写一堆重复的数据库操作代码。简单来说,你只需要专注写 SQL 语句,MyBatis 会帮你把这些 SQL 和 Java 对象自动关联起来。
MyBatis 的亮点:
- 用原生 SQL:你可以直接写熟悉的 SQL 语句,不需要学新的语法,MyBatis 帮你处理把查询结果映射到 Java 对象的麻烦。
- 灵活好用:对于复杂查询,MyBatis 非常灵活,不像一些全自动的框架那样限制多。
- 配置简单:通过简单的 XML 文件或注解,你就能快速搞定 SQL 和 Java 之间的映射关系。
- 动态 SQL:你可以根据不同条件动态生成 SQL,处理一些有条件的查询特别方便。
- 和 Spring 兼容:MyBatis 可以很好地和 Spring 一起用,整合事务管理、依赖注入这些功能。
总之,MyBatis 适合那些想要灵活控制数据库查询,又不想写太多重复代码的人。
2. MyBatis 框架的应用场景?
- 复杂 SQL 查询:需要灵活编写复杂 SQL,避免 ORM 生成低效查询。
- 现有数据库集成:对已有数据库结构进行操作,不适合全面使用 ORM 的场景。
- 高性能需求:需要手动优化 SQL 查询性能时。
- 灵活控制:需要精确控制 SQL 语句和数据库操作的应用。
它特别适合那些对 SQL 有较高控制要求的项目。
3. MyBatis 有哪些优点?
- 原生 SQL 灵活:可以手写 SQL,处理复杂查询。
- 轻量易用:配置简单,学习成本低。
- 动态 SQL 支持:根据条件动态生成 SQL。
- 高性能:手动优化 SQL,性能可控。
- 现有数据库友好:适合已有复杂数据库的项目。
4. MyBatis 有哪些缺点?
- SQL 手动编写:需要开发者手写 SQL,增加工作量。
- 维护成本高:复杂项目中,管理大量 SQL 语句和映射关系可能变得困难。
- 缺少自动化:不像 ORM 框架,无法自动生成 SQL,手动处理对象与数据库的映射。
- SQL 与代码耦合:SQL 与 Java 代码耦合紧密,不利于数据库迁移或改动。
这些缺点使 MyBatis 在一些复杂或大规模项目中可能增加开发和维护难度。
5. MyBatis 用到了哪些设计模式?
MyBatis 采用了多种设计模式,帮助其实现灵活性和可扩展性,主要包括以下几种:
-
代理模式:MyBatis 使用代理模式(动态代理)来为 Mapper 接口生成实现类,处理 SQL 语句的执行,避免手动编写实现代码。
-
工厂模式 :MyBatis 使用工厂模式创建 SQLSession 对象,通过
SqlSessionFactory
来管理和提供 SQLSession。 -
单例模式 :
SqlSessionFactory
使用单例模式确保工厂实例在应用中只有一个,方便统一管理数据库会话。 -
模板方法模式 :MyBatis 中的
Executor
类使用模板方法模式,提供执行 SQL 操作的通用流程,子类可以自定义具体实现。 -
建造者模式:在配置解析中,MyBatis 使用建造者模式生成复杂的对象,比如解析 XML 或注解并构建相应的映射配置。
6. MyBatis常用注解有哪些?
MyBatis 常用注解有:
- @Select:执行查询操作。
- @Insert:执行插入操作。
- @Update:执行更新操作。
- @Delete:执行删除操作。
- @Results:自定义结果集的映射。
- @Result:定义单个字段的映射关系。
- @Param:为 SQL 语句传递参数。
这些注解用于简化 SQL 操作,替代 XML 配置。
7. MyBatis 有哪些核心组件?
- SqlSessionFactory :创建和管理
SqlSession
对象的工厂。 - SqlSession:执行 SQL 语句的接口,管理数据库会话。
- Executor:负责执行 SQL 语句,管理缓存和事务。
- Mapper 接口:映射 SQL 语句与 Java 对象的方法接口。
- Configuration:全局配置类,包含 MyBatis 的所有配置信息。
8. MyBatis编程步骤是什么样的?
MyBatis 的编程步骤如下:
-
引入 MyBatis 依赖:在项目中配置 MyBatis 所需的依赖(如 Maven 中添加依赖项)。
-
配置 MyBatis 环境 :编写
mybatis-config.xml
,配置数据库连接信息、别名、映射文件等。 -
编写 Mapper 文件:
- 使用 XML 文件编写 SQL 映射(或用注解方式)。
- 定义接口(Mapper),与 SQL 映射文件中的 SQL 语句进行绑定。
-
创建 SqlSessionFactory :通过
SqlSessionFactoryBuilder
读取配置文件,生成SqlSessionFactory
。 -
使用 SqlSession 操作数据库:
- 通过
SqlSessionFactory
获取SqlSession
。 - 调用 Mapper 接口执行增删改查操作。
- 最后关闭
SqlSession
以释放资源。
- 通过
-
处理结果:接收数据库操作返回的结果并处理。
9. MyBatis 和JDBC有什么区别?
MyBatis 和 JDBC 的区别主要有:
- 代码简化:MyBatis 简化了 JDBC 中繁琐的数据库操作代码,如连接管理、结果集处理,而 JDBC 需要手动编写大量代码。
- SQL 映射:MyBatis 自动将 SQL 结果映射到 Java 对象,JDBC 需要手动解析结果集。
- 动态 SQL 支持:MyBatis 支持动态生成 SQL,而 JDBC 中 SQL 是固定的,需要手动拼接。
- 事务管理:MyBatis 集成了事务管理,JDBC 需要手动处理事务。
总之,MyBatis 提供了更多的自动化和简化,而 JDBC 需要更多手动控制。
10. MyBatis 中的缓存机制有啥用?
MyBatis 中的缓存机制用于提高查询性能,减少对数据库的重复访问。它包括:
- 一级缓存:SqlSession 级别的缓存,默认开启,同一个 SqlSession 中的相同查询结果会缓存,减少重复查询。
- 二级缓存:Mapper 级别的缓存,可以跨 SqlSession,缓存同一个 Mapper 下的查询结果,需手动配置。
缓存机制减少数据库压力,提升应用性能。
11. MyBatis 一级缓存和二级缓存的区别?
MyBatis 一级缓存和二级缓存的区别:
-
缓存范围:
- 一级缓存:SqlSession 级别,默认开启,作用范围仅在同一个 SqlSession 内。
- 二级缓存:Mapper 级别,可以跨 SqlSession,需手动配置。
-
生效时机:
- 一级缓存:每次使用同一个 SqlSession 执行相同查询时生效。
- 二级缓存:不同 SqlSession 执行相同查询时也可生效。
-
失效条件:
- 一级缓存:SqlSession 关闭、提交或执行更新操作后失效。
- 二级缓存:数据更新后失效,但配置持久化时缓存内容可保存。
12. MyBatis 一级缓存和二级缓存是什么数据结构?
MyBatis 一级缓存和二级缓存都使用 HashMap 作为数据结构来存储缓存数据。
13. MyBatis 中的缓存有哪些实现类型?
MyBatis 中的缓存实现类型主要有两种:
- PerpetualCache(永久缓存):默认的缓存实现,基于 HashMap 存储数据。
- Decorator 缓存 :通过装饰器模式增强缓存功能,包括:
- LruCache:最近最少使用缓存淘汰策略。
- FifoCache:先进先出缓存淘汰策略。
- SoftCache:使用软引用,内存不足时自动回收。
- WeakCache:使用弱引用,GC 后自动回收。
- ScheduledCache:定时清理缓存。
- SynchronizedCache:线程安全的缓存。
- SerializedCache:序列化存储缓存内容。
这些类型提供了不同的缓存管理策略,适应不同的需求。
14. MyBatis 默认会开启缓存机制吗? 怎么开启?
MyBatis 默认开启一级缓存,一级缓存是 SqlSession 级别的,无需额外配置。
二级缓存默认关闭,需要手动开启。开启方法如下:
-
在
mybatis-config.xml
中开启全局二级缓存:xml<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在对应的 Mapper 文件或 Mapper 接口中,通过
@CacheNamespace
注解或 XML 配置开启二级缓存:xml<cache/>
这样 MyBatis 的二级缓存就会被启用。
15. MyBatis 为什么默认不会开启二级缓存?
MyBatis 默认不启用二级缓存,主要是出于以下考虑:
-
数据一致性问题:二级缓存是跨 SqlSession 的,数据更新后如果没有及时刷新缓存,可能导致脏数据(缓存与数据库不一致)。
-
内存占用:二级缓存存储在内存中,可能会占用较多内存资源,影响系统性能。
-
适用场景有限:二级缓存适合查询频繁、更新较少的场景,但并不适合所有项目,默认关闭可以避免不必要的性能开销。
因此,MyBatis 让开发者根据实际需求手动开启二级缓存。
16. MyBatis 中的缓存什么时候会被清理?
MyBatis 中缓存的清理时机如下:
-
一级缓存(SqlSession 级别缓存):
- 当
SqlSession
关闭时,一级缓存会被清理。 - 当在同一个
SqlSession
中执行INSERT
、UPDATE
或DELETE
操作时,一级缓存会自动清空,确保数据一致性。 - 当手动调用
clearCache()
方法时,也会清理一级缓存。
- 当
-
二级缓存(Mapper 级别缓存):
- 当执行
INSERT
、UPDATE
或DELETE
操作时,二级缓存会被清除,确保数据一致性。 - 二级缓存也可以通过自定义的缓存策略(如时间策略或大小限制)自动清理。
- 如果使用了带有过期策略的缓存装饰器(如
ScheduledCache
),缓存会根据设定的时间间隔清理。 - 可以通过调用
clearCache()
方法手动清除二级缓存。
- 当执行
总结来说,缓存会在数据库发生修改操作或通过特定策略、手动清理时被清理,以保证数据的一致性和缓存的有效性。
17. MyBatis 二级缓存清理策略有哪些?
MyBatis 二级缓存的清理策略主要有:
- 自动清理 :执行
INSERT
、UPDATE
或DELETE
操作时,缓存会自动清空。 - 基于时间的清理 :使用
ScheduledCache
,按照设定的时间间隔清理缓存。 - 基于缓存淘汰策略 :
- LruCache:最近最少使用的缓存项会被清除。
- FifoCache:按照先入先出的顺序清理缓存。
- 手动清理 :可以通过调用
clearCache()
方法手动清除缓存。
18. MyBatis 接口绑定有哪几种方式?
MyBatis 接口绑定有两种方式:
- XML 文件方式:在 Mapper 接口中定义方法,并通过对应的 XML 文件绑定 SQL 语句。
- 注解方式 :直接在 Mapper 接口的方法上使用注解(如
@Select
、@Insert
、@Update
、@Delete
等)编写 SQL,实现绑定。
这两种方式都可以将 SQL 语句与接口方法关联起来。
19. MyBatis 有哪几种 SQL 编写形式?
MyBatis 支持以下两种 SQL 编写形式:
- XML 文件形式:在 XML 文件中编写 SQL 语句,通过 Mapper 接口方法绑定。
- 注解形式 :直接在 Mapper 接口的方法上使用注解(如
@Select
、@Insert
、@Update
、@Delete
)编写 SQL。
这两种方式都可以用于实现 SQL 操作,选择哪种形式取决于项目需求。
20. MyBatis 映射文件中有哪些顶级元素?
MyBatis 映射文件中的顶级元素主要包括:
<cache>
:配置缓存策略。<cache-ref>
:引用其他命名空间的缓存。<resultMap>
:定义结果集映射规则。<sql>
:定义可复用的 SQL 片段。<insert>
、<update>
、<delete>
、<select>
:定义 SQL 操作语句。
这些元素是 MyBatis 映射文件的核心组成部分。
21. MyBatis 映射时 A 引用了 B,如果 B 在 A 后面会怎样?
在 MyBatis 映射文件中,如果映射 A
引用了映射 B
,而 B
的定义在 A
之后,通常会导致 MyBatis 报错。因为 MyBatis 解析映射文件时是顺序进行的,当解析到 A
时,它会试图找到 B
,但此时 B
还未被解析,因此会抛出 Invalid Reference
等类似的错误。
为避免这种问题,可以通过以下方式解决:
- 调整顺序 :确保
B
在A
之前定义。 - 使用命名空间引用 :将
B
放在其他映射文件中并通过命名空间引用,确保引用的映射在加载时已经存在。
顺序错误会直接影响 MyBatis 的解析,因此需要特别注意映射文件中引用的顺序。
22. MyBatis 中 Mapper 接口的实现原理是?
MyBatis 中 Mapper 接口的实现原理是通过动态代理生成接口的实现类。MyBatis 根据 Mapper 接口的方法签名自动生成 SQL 语句,并在运行时由 SqlSession
提供执行,返回对应的查询结果。这样开发者只需定义接口和 SQL 映射,不需要手动编写实现类。
23. MyBatis用注解绑定和用XML文件绑定有什么区别?
MyBatis 中注解绑定和 XML 文件绑定的区别如下:
-
注解绑定:
- SQL 语句直接写在 Mapper 接口的方法上,通过注解(如
@Select
,@Insert
,@Update
,@Delete
)定义。 - 适合简单的 SQL 语句,代码集中在一个地方,维护方便。
- 适合轻量级项目,不需要单独的 XML 文件。
- SQL 语句直接写在 Mapper 接口的方法上,通过注解(如
-
XML 文件绑定:
- SQL 语句保存在独立的 XML 文件中,通过
<select>
,<insert>
,<update>
,<delete>
等标签定义。 - 适合复杂 SQL,尤其是动态 SQL,XML 提供更灵活的控制。
- 便于与业务逻辑分离,SQL 语句与代码解耦,易于维护和扩展。
- SQL 语句保存在独立的 XML 文件中,通过
简而言之,注解适合简单 SQL,XML 更适合复杂 SQL 和动态 SQL。
24. MyBatis通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重载?
在 MyBatis 中,每个 XML 映射文件通常对应一个 DAO(Data Access Object)接口。DAO 接口的工作原理 是通过 MyBatis 的动态代理机制:MyBatis 自动生成 DAO 接口的实现类,通过 SqlSession
执行映射文件中的 SQL 语句,并将结果返回给调用者。
DAO 接口中的方法可以重载,即在同一个接口中,可以定义多个方法名相同但参数类型或数量不同的方法。然而,需要注意的是:
- MyBatis 会根据方法签名(包括方法名和参数类型)确定调用哪个 SQL 语句。
- 在 XML 映射文件中,每个重载的方法需要对应唯一的
<select>
,<insert>
,<update>
,<delete>
标签,这些标签需要通过id
唯一标识,每个id
应与重载方法匹配。
因此,DAO 接口方法可以重载,但需要确保 XML 映射文件中的 id
能唯一标识每个方法。
25. MyBatis 中 Mapper 中的 SQL 语句可以重载吗?
MyBatis 中 SQL 语句不能直接重载 ,因为 SQL 是通过 Mapper
方法的名字和参数唯一映射的。如果方法重载,MyBatis 无法区分不同方法的 SQL。
解决方式:
- 使用不同的方法名。
- 使用动态 SQL 实现条件判断。
- 使用
@Param
区分参数。
重载本质上需要避免方法和 SQL 映射冲突。
26. MyBatis动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
MyBatis 动态 SQL 用于根据条件动态生成 SQL 语句,避免写多个 SQL。
常用动态 SQL:
<if>
:条件判断。<choose>
:类似switch-case
。<trim>
:去除多余的前后缀。<where>
:自动添加WHERE
。<set>
:处理UPDATE
语句。<foreach>
:循环生成 SQL,常用于IN
子句。
执行原理:根据传入参数,动态解析 SQL 标签生成最终 SQL,并执行。
27. MyBatis实体类中的属性名和表中的字段名不一样 ,怎么办?
在 MyBatis 中,如果实体类的属性名与数据库表中的字段名不一致,可以通过以下两种方式进行映射:
-
使用
@Results
注解 :在 Mapper 接口的方法上通过
@Results
注解手动指定实体类属性与数据库字段的对应关系。java@Select("SELECT id, user_name FROM users") @Results({ @Result(property = "userName", column = "user_name") }) List<User> selectUsers();
-
在 XML 映射文件中使用
<resultMap>
:通过 XML 配置文件的
<resultMap>
元素将表字段与实体类属性进行映射。xml<resultMap id="userMap" type="User"> <result property="userName" column="user_name"/> </resultMap> <select id="selectUsers" resultMap="userMap"> SELECT id, user_name FROM users </select>
这两种方式都可以解决实体类属性名和表字段名不一致的问题。
28. MyBatis 配置文件中的 SQL id 是否能重复?
在 MyBatis 配置文件中,SQL id
不能重复 。id
是每个 SQL 语句的唯一标识,用于在 Mapper
接口中引用特定的 SQL 语句。如果同一个 id
重复,MyBatis 会抛出异常,导致系统无法正常工作。
因此,确保每个 id
在同一 Mapper
文件中都是唯一的,以避免冲突。
29. MyBatis 支持哪些传参数的方法?
MyBatis 支持以下几种传参方法:
-
单个参数 :传递简单类型或对象,SQL 中用
#{param}
访问。 -
多个参数 :使用
@Param
注解,例如:javaList<User> findUser(@Param("id") int id, @Param("name") String name);
-
传递对象 :传递实体对象,SQL 中用
#{user.id}
访问属性。 -
使用
Map
传参 :通过Map
传递参数,SQL 中用#{key}
访问值。
这些方法提供了灵活的参数传递方式。
30. MyBatis 的$和# 传参的区别?
在 MyBatis 中,$
和 #
传参的区别如下:
-
#
(安全的参数占位符):- 使用
#
时,MyBatis 会对传入的参数进行预处理,防止 SQL 注入。 - 适用于 SQL 语句中的参数替换,例如:
SELECT * FROM users WHERE id = #{id}
。
- 使用
-
$
(直接字符串替换):- 使用
$
时,MyBatis 直接将参数作为字符串替换,不进行任何处理。 - 适用于动态表名、列名等场景,例如:
SELECT * FROM ${tableName}
。 - 由于不安全,容易引发 SQL 注入,需谨慎使用。
- 使用
31. MyBatis 可以映射到枚举类吗?
-
默认映射:
- 如果枚举名称与数据库字段值相同,MyBatis 会自动映射。
javapublic enum Status { ACTIVE, INACTIVE; }
-
自定义类型处理器:
- 如果需要自定义映射,可以实现
BaseTypeHandler
。
javapublic class StatusTypeHandler extends BaseTypeHandler<Status> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Status parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter.name()); } @Override public Status getNullableResult(ResultSet rs, String columnName) throws SQLException { return Status.valueOf(rs.getString(columnName)); } }
- 如果需要自定义映射,可以实现
-
在 XML 中指定类型处理器:
- 在 SQL 映射中指定使用自定义类型处理器。
xml<resultMap id="userMap" type="User"> <result property="status" column="status_column" typeHandler="com.example.StatusTypeHandler"/> </resultMap>
总结:MyBatis 支持直接映射枚举或使用自定义类型处理器进行映射。
32. MyBatis 怎么封装动态 SQL?
在 MyBatis 中,封装动态 SQL 可以通过以下几种方式实现:
- XML 映射文件
使用动态 SQL 标签,如<if>
和<choose>
:
xml
<select id="findUsers" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="status != null">
AND status = #{status}
</if>
</select>
- 注解方式
在 Mapper 接口中使用注解和脚本:
java
@Select({
"<script>",
"SELECT * FROM users",
"WHERE 1=1",
"<if test='name != null'>",
"AND name = #{name}",
"</if>",
"<if test='status != null'>",
"AND status = #{status}",
"</if>",
"</script>"
})
List<User> findUsers(@Param("name") String name, @Param("status") String status);
- 查询条件对象
定义一个查询条件对象,封装参数:
java
public class UserQuery {
private String name;
private String status;
// getters and setters
}
在 XML 中使用:
xml
<select id="findUsers" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
<if test="status != null">
AND status = #{status}
</if>
</select>
33. MyBatis trim 标签有什么用?
MyBatis 中的 <trim>
标签用于处理 SQL 语句的前后缀,简化 SQL 的构建过程。它的主要作用是去除多余的分隔符(如 AND
、OR
),确保生成的 SQL 语句语法正确。
主要功能:
-
去除前缀:
- 可以在 SQL 语句开始前去除特定的前缀(如
AND
或OR
)。
- 可以在 SQL 语句开始前去除特定的前缀(如
-
去除后缀:
- 可以在 SQL 语句结束后去除特定的后缀(如
AND
或OR
)。
- 可以在 SQL 语句结束后去除特定的后缀(如
-
处理空值:
- 只有在满足某个条件时,才会添加对应的 SQL 部分。
示例:
xml
<select id="findUsers" resultType="User">
SELECT * FROM users
<where>
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="name != null">
AND name = #{name}
</if>
<if test="status != null">
AND status = #{status}
</if>
</trim>
</trim>
</select>
总结:<trim>
标签可以有效管理 SQL 语句中的多余分隔符,使动态 SQL 的构建更加灵活和简洁。
34. MyBatis where 标签有什么用?
MyBatis 中的 <where>
标签用于自动处理 SQL 语句中的 WHERE
子句,主要功能包括:
- 自动添加
WHERE
:在有条件时自动插入WHERE
关键字。 - 去除多余的
AND
或OR
:如果条件成立,自动去除多余的AND
或OR
,确保 SQL 语法正确。
示例:
xml
<select id="findUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
35. MyBatis 是如何进行分页的?分页插件的原理是什么?
MyBatis 进行分页的常用方式是通过 分页插件 ,如 PageHelper。以下是其基本步骤和原理:
- 使用步骤
-
添加依赖:在项目中添加分页插件的依赖。
-
配置插件:在 MyBatis 配置文件中注册分页插件。
xml<plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> <!-- 数据库类型 --> </plugin> </plugins>
-
调用分页方法:在查询前设置分页参数。
javaPageHelper.startPage(pageNum, pageSize); // 设置当前页和每页大小 List<User> users = userMapper.findAll(); // 查询数据
- 原理
- 拦截器:分页插件通过 MyBatis 的插件机制拦截 SQL 执行。
- SQL 修改 :插件修改原始 SQL,添加
LIMIT
和OFFSET
以实现分页。 - 结果封装:将结果封装成分页对象,包含总记录数和分页数据。
36. MyBatis 有几种分页方式?
MyBatis 有以下几种分页方式:
-
使用分页插件 :如 PageHelper,通过拦截器自动处理 SQL 进行分页。
-
手动分页 :在 SQL 中手动添加
LIMIT
和OFFSET
,根据传入的参数控制分页。 -
使用 RowBounds :使用
RowBounds
对象进行内存中的分页,不改变原始 SQL。
37. MyBatis 逻辑分页和物理分页的区别是什么?
- 逻辑分页
- 定义:逻辑分页是指在应用层面进行分页处理,查询所有数据后再在内存中进行分页。
- 特点 :
- 所有数据一次性加载到内存中,适合数据量小的场景。
- 可能会导致性能问题,因为需要处理完整的数据集。
- 实现方式 :通过
RowBounds
或手动切分结果集。
- 物理分页
- 定义:物理分页是指在数据库层面通过 SQL 语句进行分页,仅查询需要的数据。
- 特点 :
- 只查询当前页的数据,节省了内存和网络传输资源。
- 更适合大数据量的场景,性能更优。
- 实现方式 :通过
LIMIT
和OFFSET
或使用分页插件(如 PageHelper)。
- 总结
- 逻辑分页:在应用层处理,适合小数据集,可能影响性能。
- 物理分页:在数据库层处理,适合大数据集,性能更佳。
38. MyBatis 流式查询有什么用?
-
减少内存占用
逐行处理查询结果,避免一次性加载所有数据到内存中。
-
提高性能
允许在结果加载时进行后续操作,缩短响应时间。
-
适用于大数据集
有效解决无法一次性加载大数据集的问题。
-
示例
使用
ResultHandler
逐行处理结果:
java
sqlSession.select("namespace.selectStatement", new ResultHandler<User>() {
@Override
public void handleResult(ResultContext<? extends User> resultContext) {
User user = resultContext.getResultObject();
// 逐行处理 user 对象
}
});
39. MyBatis 模糊查询 like 语该怎么写?
在 MyBatis 中,进行模糊查询可以使用 LIKE
关键字,通常结合 SQL 的通配符 %
来实现。
- XML 映射文件中的模糊查询
xml
<select id="findUsersByName" resultType="User">
SELECT * FROM users
WHERE name LIKE CONCAT('%', #{name}, '%')
</select>
- 使用注解的方式
java
@Select("SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')")
List<User> findUsersByName(@Param("name") String name);
- 传递参数
在调用时,将需要模糊匹配的字符串传入查询方法中:
java
List<User> users = userMapper.findUsersByName("张三");
总结:使用 LIKE
结合 %
通配符,可以在 MyBatis 中实现模糊查询。通过 XML 或注解都可以轻松实现。
40. MyBatis 如何防止 SQL 注入?
MyBatis 防止 SQL 注入主要通过以下方式:
-
使用预编译语句 :通过参数化查询(
#{}
)来绑定参数,避免直接拼接 SQL 字符串。xml<select id="findUser" resultType="User"> SELECT * FROM users WHERE username = #{username} </select>
-
避免直接拼接 SQL:不使用字符串拼接构建 SQL 语句,确保输入参数经过处理。
-
使用 MyBatis 的内置功能 :使用内置的
@Param
注解来安全地传递参数。 -
输入验证:在应用层进行输入验证,确保参数合法。
总结:通过参数化查询、避免拼接 SQL 和输入验证,MyBatis 有效防止 SQL 注入。
41. MyBatis 如何获取自动生成的主键id?
在 MyBatis 中获取自动生成的主键 ID 的方法如下:
- 使用 XML 映射
xml
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (username, password) VALUES (#{username}, #{password})
</insert>
- 使用注解
java
@Insert("INSERT INTO users (username, password) VALUES (#{username}, #{password})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
- 获取 ID
插入后,自动生成的主键会赋值给对象的 id
属性:
java
User user = new User();
user.setUsername("张三");
user.setPassword("password");
userMapper.insertUser(user);
Long generatedId = user.getId(); // 获取主键 ID
42. MyBatis 中jdbcType
和javaType
的区别?
jdbcType
- 定义:表示数据库中列的数据类型。
- 用途:用于确定如何处理数据库中的数据,影响 SQL 语句的生成和执行。
- 示例 :如
VARCHAR
、INTEGER
、DATE
等。
javaType
- 定义:表示 Java 中对应的数据类型。
- 用途:用于将数据库中的数据映射到 Java 对象的属性。
- 示例 :如
String
、Integer
、Date
等。
总的来说,javaType
和jdbcType
都是用来处理数据类型的,javaType
主要用来处理Java对象到数据库的映射,而jdbcType
主要用来处理数据库类型和JDBC之间的关系。
43. 什么时候必须指定jdbcType
和javaType
?
在 MyBatis 中,必须指定 jdbcType
和 javaType
的情况主要有以下几种:
jdbcType
- NULL 值处理 :当字段可以为
NULL
时,必须显式指定jdbcType
,以便正确处理空值。 - 特定类型映射 :对于一些特殊或不常用的数据类型,如
BLOB
、CLOB
等,建议指定jdbcType
以确保正确处理。
javaType
- 复杂类型 :当字段类型是复杂类型(如自定义对象或集合)时,必须指定
javaType
,以确保正确映射到 Java 类型。 - 不匹配类型 :当数据库字段类型与 Java 类型不一致时,需要明确指定
javaType
以避免类型转换错误。
示例
xml
<resultMap id="UserMap" type="User">
<result property="id" column="id" jdbcType="INTEGER" javaType="java.lang.Integer"/>
<result property="username" column="username" jdbcType="VARCHAR" javaType="java.lang.String"/>
<result property="createdDate" column="created_date" jdbcType="TIMESTAMP" javaType="java.util.Date"/>
</resultMap>
总结:必须在处理 NULL
值、复杂类型或类型不匹配时显式指定 jdbcType
和 javaType
。
44. MyBatis 支持预编译吗? 怎么做?
MyBatis 支持预编译,主要通过参数化查询来实现。具体做法如下:
- 使用参数化查询
使用#{}
语法指定参数,MyBatis 会自动进行预编译。
示例
XML 映射文件
xml
<select id="findUserById" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
Mapper 接口
java
User findUserById(@Param("id") int id);
总结:MyBatis 通过参数化查询(#{}
)实现预编译,确保 SQL 安全性和性能优化。
45. MyBatis 中的事务管理方式?
- 编程式事务管理
- 通过
SqlSession
控制事务 :- 手动控制事务的开始、提交和回滚。
- 适合需要细粒度控制的场景。
java
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 开始事务
// 执行操作
sqlSession.commit(); // 提交事务
} catch (Exception e) {
sqlSession.rollback(); // 回滚事务
} finally {
sqlSession.close();
}
- 声明式事务管理
- 结合 Spring 框架使用 :
- 通过
@Transactional
注解自动管理事务。 - 适合简单配置和一致性需求的场景。
- 通过
java
@Service
public class UserService {
@Transactional
public void addUser(User user) {
// 执行数据库操作
}
}
总结:MyBatis 提供编程式和声明式两种事务管理方式,编程式适合细粒度控制,声明式适合与 Spring 结合使用。
46. 怎么开启事务?
- 编程式事务管理
java
SqlSession sqlSession = sqlSessionFactory.openSession(); // 开启事务
try {
// 执行数据库操作
sqlSession.commit(); // 提交事务
} catch (Exception e) {
sqlSession.rollback(); // 回滚事务
} finally {
sqlSession.close(); // 关闭会话
}
- 声明式事务管理(与 Spring 结合使用)
java
@Service
public class UserService {
@Transactional // 开启事务
public void addUser(User user) {
// 执行数据库操作
}
}
总结
- 编程式 :使用
SqlSession
手动管理事务。 - 声明式 :使用
@Transactional
注解自动处理事务。
47. MyBatis 事务和 Spring 事务有什么区别?
- 事务管理方式
-
MyBatis 事务:
- 主要通过
SqlSession
手动管理,支持编程式事务控制。 - 事务的开始、提交和回滚需要显式调用。
- 主要通过
-
Spring 事务:
- 提供了声明式事务管理,可以通过
@Transactional
注解自动处理事务。 - 可以与多种数据访问技术(如 JPA、Hibernate)结合使用。
- 提供了声明式事务管理,可以通过
- 事务范围
-
MyBatis 事务:
- 主要针对 MyBatis 的操作,事务的控制相对简单。
-
Spring 事务:
- 可以跨多个持久化层和服务层操作,支持更复杂的事务管理。
- 事务传播行为
-
MyBatis 事务:
- 没有内置的事务传播机制,需要手动实现。
-
Spring 事务:
- 提供多种事务传播行为(如
REQUIRED
、REQUIRES_NEW
),可以灵活管理事务的传播和隔离级别。
- 提供多种事务传播行为(如
- 集成支持
-
MyBatis 事务:
- 主要与 MyBatis 自身的会话管理结合。
-
Spring 事务:
- 支持多种持久化框架和不同的事务管理器,易于集成。
总结:
- MyBatis 事务:手动控制,适合简单场景。
- Spring 事务:声明式管理,支持复杂事务和多种持久化框架。
48. MyBatis 使用了哪些设计模式?
MyBatis 使用了以下设计模式:
-
代理模式:通过动态代理实现接口的实现,简化数据库操作。
-
工厂模式 :使用
SqlSessionFactory
创建SqlSession
,解耦对象创建过程。 -
单例模式 :
SqlSessionFactory
通常采用单例模式,以确保只创建一个实例,优化资源使用。 -
模板方法模式:定义了操作的框架,允许子类(具体实现)重写某些步骤,但不改变算法的结构。
-
建造者模式:在创建复杂 SQL 语句时,通过构建器模式分步构造 SQL 语句。
总结:MyBatis 利用代理、工厂、单例、模板方法和建造者等设计模式,实现了灵活、可扩展和易于维护的数据库访问框架。
49. 什么是 JPA?
JPA(Java Persistence API)是 Java 平台的标准规范,用于管理 Java 对象与数据库之间的持久化。它主要特点包括:
- 对象关系映射(ORM):将 Java 对象映射到数据库表。
- 持久化上下文:管理实体生命周期,确保数据一致性。
- 查询语言:提供 JPQL(Java Persistence Query Language)用于查询实体。
- 事务管理:支持与 JTA 集成,管理事务。
- 厂商独立性:支持多种实现(如 Hibernate、EclipseLink)。
总结:JPA 简化了 Java 应用程序的数据库操作,提供标准化的持久化解决方案。
50. MyBatis 中 StatementHandler
和 MappedStatement
区别?
在 MyBatis 中,StatementHandler
和 MappedStatement
的区别如下:
- MappedStatement
- 定义:表示 SQL 映射语句的元数据,包括 SQL 语句、参数类型、返回类型等信息。
- 作用:用于配置和描述特定的 SQL 语句及其相关信息,通常在 XML 映射文件或注解中定义。
- StatementHandler
- 定义 :用于执行 SQL 语句的接口,负责创建和管理
PreparedStatement
。 - 作用:处理 SQL 执行的具体逻辑,包括参数设置、执行查询和处理结果集。
总结:
- MappedStatement:用于描述 SQL 映射及其元数据。
- StatementHandler:负责执行 SQL 语句的具体实现。
51. MyBatis 常用的 TypeHandler
有哪些?
MyBatis 常用的 TypeHandler
包括以下几种:
-
BasicTypeHandler:
- 默认的类型处理器,处理基本类型(如
int
、String
等)与数据库类型之间的映射。
- 默认的类型处理器,处理基本类型(如
-
EnumTypeHandler:
- 用于处理 Java 枚举类型与数据库值之间的映射。
-
DateTypeHandler:
- 用于处理
java.util.Date
、java.sql.Date
和java.sql.Timestamp
等日期类型。
- 用于处理
-
JsonTypeHandler:
- 用于处理 JSON 字符串与 Java 对象之间的映射,通常与 JSON 库(如 Jackson 或 Gson)结合使用。
-
BlobTypeHandler:
- 用于处理 BLOB(二进制大对象)数据类型的映射,适合存储二进制数据。
-
ClobTypeHandler:
- 用于处理 CLOB(字符大对象)数据类型的映射,适合存储大文本数据。
52. MyBatis 执行流程是怎样的?
MyBatis 的执行流程如下:
-
创建 SqlSessionFactory:
- 从 MyBatis 配置文件中读取配置信息,创建
SqlSessionFactory
实例。
- 从 MyBatis 配置文件中读取配置信息,创建
-
打开 SqlSession:
- 通过
SqlSessionFactory
打开一个SqlSession
,开始数据库操作。
- 通过
-
映射语句的调用:
- 调用 Mapper 接口中的方法,传入参数,MyBatis 根据方法名查找对应的
MappedStatement
。
- 调用 Mapper 接口中的方法,传入参数,MyBatis 根据方法名查找对应的
-
执行 SQL 语句:
StatementHandler
根据MappedStatement
创建 SQL 语句,并执行数据库操作(如查询、插入、更新等)。
-
处理结果:
- 将数据库返回的结果集映射为 Java 对象(实体),并返回给调用者。
-
提交或回滚事务:
- 如果是手动管理事务,根据操作结果选择提交或回滚事务。
-
关闭 SqlSession:
- 关闭
SqlSession
,释放资源。
- 关闭
总结:MyBatis 的执行流程从创建 SqlSessionFactory
到关闭 SqlSession
,包括 SQL 映射、执行和结果处理等步骤。
53. MyBatis 中的 SglSession
是线程安全的吗?
MyBatis 中的 SqlSession
不是线程安全 的。每个 SqlSession
实例应当在一个线程内独立使用,不能被多个线程共享。为了确保线程安全,通常的做法是每个线程都创建自己的 SqlSession
实例,使用完成后及时关闭。
54. MyBatis 中的 SglSession
有哪些实现类?
在 MyBatis 中,SqlSession
的主要实现类包括:
-
DefaultSqlSession:
- MyBatis 默认的
SqlSession
实现,提供了基本的数据库操作功能。
- MyBatis 默认的
-
SqlSessionManager:
- 用于管理多个
SqlSession
的生命周期,支持事务控制。
- 用于管理多个
-
SqlSessionTemplate(Spring 集成):
- 提供线程安全的
SqlSession
实现,适用于 Spring 环境,支持 Spring 的事务管理。
- 提供线程安全的
55. MyBatis 中的 DefaultSqlSession
为什么不是线程安全的?
DefaultSqlSession
在 MyBatis 中不是线程安全的,主要原因包括:
-
状态管理:
DefaultSqlSession
维护了内部状态,如连接、事务和缓存等,这些状态在多个线程中共享可能导致数据不一致。
-
资源共享:
- 它包含对数据库连接的引用和其他资源,多个线程同时访问可能导致并发问题。
-
设计目的:
SqlSession
设计为短暂的、一次性的会话,旨在处理单一数据库操作,适合在一个线程中使用。
56. MyBatis 中 SqlSessionTemplate
与 SqlSessionManager
的区别?
SqlSessionTemplate
和 SqlSessionManager
在 MyBatis 中的区别如下:
- 用途
-
SqlSessionTemplate:
- 用于在 Spring 环境中提供线程安全的
SqlSession
,支持 Spring 的事务管理。适合在 Spring Bean 中使用。
- 用于在 Spring 环境中提供线程安全的
-
SqlSessionManager:
- 用于管理多个
SqlSession
的生命周期,提供事务控制和批量处理功能。适合在需要手动管理SqlSession
的场景中使用。
- 用于管理多个
- 线程安全
-
SqlSessionTemplate:
- 线程安全,可以被多个线程共享,适合在多线程环境中使用。
-
SqlSessionManager:
- 本身并不保证线程安全,通常在每个线程中创建独立的
SqlSession
。
- 本身并不保证线程安全,通常在每个线程中创建独立的
- 事务管理
-
SqlSessionTemplate:
- 自动与 Spring 的事务管理集成,简化事务处理。
-
SqlSessionManager:
- 提供手动的事务控制,适合复杂的事务管理场景。
总结:
- SqlSessionTemplate:线程安全,适合 Spring 环境,自动集成事务管理。
- SqlSessionManager :管理多个
SqlSession
,不保证线程安全,适合手动事务控制。