这些都是面试常见的问题,看看下面的问题你都能答得上来吗?
1.什么是 MyBatis?它有哪些主要特性和优势?
2. mybatis 和数据库交互的原理?
3. mybatis 中#{}、${}
的区别
4. Mybatis 自带连接池都有什么?
5. Mybatis 的一级、二级缓存?
6. MyBatis 是如何进行分页的?分页插件的原理是什么?
7. mybatis 中常见的设计模式有哪些?
1. 什么是 MyBatis?它有哪些主要特性和优势?
MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 消除了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java POJO 到数据库中的记录。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.basic.mapper.EmployeesDao">
<resultMap type="org.example.basic.entity.Employees" id="EmployeesMap">
<result property="employeeId" column="employee_id" jdbcType="INTEGER"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
</resultMap>
<select id="list" resultMap="EmployeesMap">
select
employee_id,name,password
from employees
<where>
<if test="employeeId != null">
and employee_id = #{employeeId}
</if>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
<if test="password !=null and password !=''">
and password = #{password}
</if>
</where>
</select>
</mapper>
2. mybatis 和数据库交互的原理?
mybatis 和数据库交互主要分为几个阶段,
第一步是 mybatis 会去加载配置文件,获取必要的基础的信息。
然后创建出 sqlsessionfactory 工厂,工厂会帮助我们进行环境初始化,数据源加载等等。
然后创建一个 sqlsession 对象,sqlsession 承担了与数据库交互的核心。
每次执行数据库操作,可以从 sqlsession 里面获取到需要执行的 mapper。
然后通过 mapper 开始执行 sql,获取数据。
如果涉及事务也是 session 帮助我们来做事务提交或回滚的操作。
详细流程
初始化阶段
- 加载全局配置文件 (mybatis-config.xml)
- 使用 Resources.getResourceAsStream 方法读取 MyBatis 全局配置文件。
- 解析配置文件中的
<environments>、<mappers>
等节点,加载数据源、事务管理器和 Mapper 文件。
- 创建 SqlSessionFactory
- 使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory 对象。
- SqlSessionFactory 会根据配置文件初始化 MyBatis 环境,包括数据源、事务管理器和 Mapper 文件。
获取 SqlSession
- 打开 SqlSession
- 从 SqlSessionFactory 获取一个新的 SqlSession 实例。SqlSession 是 MyBatis 与数据库交互的核心对象,负责执行 SQL 语句、获取 Mapper 接口实例、管理事务等。
Mapper 接口操作
- 获取 Mapper 接口实例
- 通过 SqlSession 获取 Mapper 接口的实现类实例。MyBatis 会在运行时生成 Mapper 接口的实现类,并通过 SqlSession 获取其实例。
执行 SQL 语句
- 调用 Mapper 方法
- 调用 Mapper 接口的方法,例如 mapper.selectUserById(1);
- 参数处理
- 将方法参数转换为 SQL 语句中的占位符(如#{id})所需的值。MyBatis 使用 TypeHandler 将 Java 类型转换为 JDBC 类型。
- 执行 SQL 语句
- MyBatis 使用 JDBC 通过数据源执行 SQL 语句,数据库返回结果集。
- 结果映射
- 将 SQL 查询结果集映射为 Java 对象。根据映射文件或注解中的配置,将结果集中的数据映射到 Java 对象的属性中。
事务管理
- 提交事务
- 如果执行的数据操作需要提交事务,调用 session.commit();提交事务,提交事务会将所有未提交的更改保存到数据库。
- 回滚事务
- 如果操作过程中发生异常,需要回滚事务,调用 session.rollback();回滚事务,回滚事务会撤销所有未提交的更改。
关闭资源
- 关闭 SqlSession
- 操作完成后,调用 session.close();关闭 SqlSession,释放数据库连接。
// 1. 加载配置文件并创建 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 3. 获取 Mapper 接口实例
UserMapper mapper = session.getMapper(UserMapper.class);
// 4. 执行 SQL 语句
User user = mapper.selectUserById(1);
System.out.println(user);
// 5. 提交事务(如果有数据修改操作)
session.commit();
} catch (Exception e) {
// 5. 回滚事务
session.rollback();
e.printStackTrace();
} finally {
// 6. 关闭 SqlSession
session.close();
}
3. mybatis 中#{}、${}
的区别
#{}
(预编译方式)
- 安全性:#{} 采用了预编译(PreparedStatement)的方式,有效防止了 SQL 注入。因为 MyBatis 会将
#{}
中的内容当作参数处理,而不是直接拼接到 SQL 语句中。 - 处理方式:MyBatis 会为
#{}
中的内容生成一个参数占位符 ?,并使用 PreparedStatement 的 setXXX() 方法来设置参数值。因此,你不需要担心数据类型或引号问题。 例子:
SELECT * FROM users WHERE id = #{userId}
,这条 SQL 语句在 MyBatis 中处理时,会将其转换为类似SELECT * FROM users WHERE id = ?
的形式,并通过 PreparedStatement 的 setInt() 或其他相关方法来设置 userId 的值。
${}
(字符串拼接方式)
- 安全性:
${}
是直接字符串拼接的方式,所以存在 SQL 注入的风险。如果参数来自用户输入或其他不可信的来源,那么使用${}
是非常危险的。 - 处理方式:MyBatis 会直接将
${}
中的内容替换到 SQL 语句中。这意味着你需要自己处理数据类型、引号等问题。 - 用途:虽然
${}
存在安全风险,但在某些场景下它是必要的。例如,当你要动态地构建表名或列名时,就必须使用${}
。 - 例子:
SELECT * FROM ${tableName}
,这里的 tableName 是一个变量,MyBatis 会直接将其替换到 SQL 语句中。因此,如果你不能保证 tableName 的来源是可信的,那么这条 SQL 语句就存在 SQL 注入的风险。
4. Mybatis 自带连接池都有什么?
MyBatis 自带的连接池实现主要有两种:PooledDataSource和UnpooledDataSource。这两种连接池实现提供了不同的特性和使用场景。
UnpooledDataSource
UnpooledDataSource 是 MyBatis 提供的一个简单的、未池化的连接池实现。每次请求数据库连接时,UnpooledDataSource 都会创建一个新的连接,而不是从连接池中获取连接。这种实现适用于对性能要求不高的小型应用或测试环境。
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
PooledDataSource
PooledDataSource 是 MyBatis 提供的一个简单的连接池实现。它维护了一组数据库连接,以便在需要时快速提供连接,而不需要每次都创建新的连接。PooledDataSource 提供了基本的连接池功能,如连接池大小、连接超时等。
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<property name="poolMaximumActiveConnections" value="10"/>
<property name="poolMaximumIdleConnections" value="5"/>
<property name="poolMaximumCheckoutTime" value="20000"/>
<property name="poolTimeToWait" value="20000"/>
</dataSource>
</environment>
选择连接池实现
虽然 MyBatis 提供了内置的连接池实现,但在生产环境中,通常推荐使用功能更强大、性能更好的第三方连接池,例如:
- HikariCP:以高性能和低延迟著称。
- Druid:一个成熟且广泛使用的连接池实现。
- C3P0:一个功能丰富的连接池实现,适用于需要高级功能的场景。
- HikariCP:以高性能和低延迟著称。
- 使用第三方连接池时,可以通过配置 MyBatis 使用外部的数据源来替代内置的连接池。
5. Mybatis 的一级、二级缓存?
1. 一级缓存
- MyBatis 的一级缓存 是用来减少数据库查询次数的,每个 SqlSession 都有自己的缓存。这个缓存默认是开启的,不需要额外配置。
- 缓存只在同一个 SqlSession 内有效,不同的 SqlSession 之间互不影响。执行增删改操作、调用 commit() 或 rollback() 方法,或者关闭 SqlSession 时,缓存会被清空。
- 当你执行查询操作时,MyBatis 会先看看缓存里有没有数据。如果有,就直接用缓存里的数据,不用再去查数据库。如果没有,就去查数据库,然后把结果放进缓存里。
- MyBatis 用查询的 SQL 语句和参数作为缓存的键。
2. 二级缓存
- mybatis 的二级缓存 ,相比与一级,其实更加的牛,相当于是跨 session 级别的缓存机制。
- 不同的 sqlsession 可以共享缓存,而在一级中,是彼此独立的。
- 二级缓存默认是关闭的,需要我们进行一波手动的开启。
- 二级缓存会将查询结果存储在内存中,以键值对的形式保存。Key 通常是查询语句及其参数的组合,Value 是查询结果。
- 当执行更新、插入或删除操作时,相关的缓存条目会被自动清除。可以集成第三方缓存框架,通过配置
<cache>
标签中的 type 属性来指定缓存提供者。
6. MyBatis 是如何进行分页的?分页插件的原理是什么?
- Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
- 分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
- 例子:
select * from student
,拦截 sql 后重写为:select t.* from (select * from student) t limit 0, 10
7. mybatis 中常见的设计模式有哪些?
mybatis 中应用了大量的设计模式,常见的有工厂模式,单例模式,代理模式等,下面是详细的常见设计模式。
1. 工厂模式
- 在创建复杂对象时使用。MyBatis 使用 SqlSessionFactory 来创建 SqlSession 实例。SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的。
2. 单例模式
- MyBatis 的 Configuration 类使用了单例模式,确保在一个 SqlSessionFactory 中只有一个 Configuration 实例。
3. 代理模式
- 为其他对象提供一种代理以控制对这个对象的访问。MyBatis 使用动态代理为 Mapper 接口生成实现类,从而在调用 Mapper 方法时执行 SQL 语句。
4. 模板方法模式
- 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。MyBatis 的 Executor 接口及其实现类使用了模板方法模式,定义了执行 SQL 语句的基本流程,而具体的执行细节由子类实现。
5. 建造者模式
- SqlSessionFactoryBuilder:MyBatis 使用 SqlSessionFactoryBuilder 来构建 SqlSessionFactory 实例。
6. 装饰器模式(Decorator Pattern)
- 插件机制:MyBatis 的插件机制使用了装饰器模式,可以在执行器、参数处理器等核心组件上添加额外的功能。
7. 策略模式(Strategy Pattern)
- 缓存策略:MyBatis 的缓存机制使用了策略模式,不同的缓存实现可以互相替换。
8. 责任链模式(Chain of Responsibility Pattern)
- 拦截器链:MyBatis 的拦截器机制使用了责任链模式,可以在执行 SQL 语句的过程中通过一系列拦截器进行处理。
就业陪跑训练营学员投稿
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:思否面试群。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。