头图

这些都是面试常见的问题,看看下面的问题你都能答得上来吗?

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 帮助我们来做事务提交或回滚的操作。

详细流程

初始化阶段
  1. 加载全局配置文件 (mybatis-config.xml)
  • 使用 Resources.getResourceAsStream 方法读取 MyBatis 全局配置文件。
  • 解析配置文件中的<environments>、<mappers>等节点,加载数据源、事务管理器和 Mapper 文件。
  1. 创建 SqlSessionFactory
  • 使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory 对象。
  • SqlSessionFactory 会根据配置文件初始化 MyBatis 环境,包括数据源、事务管理器和 Mapper 文件。
获取 SqlSession
  1. 打开 SqlSession
  • 从 SqlSessionFactory 获取一个新的 SqlSession 实例。SqlSession 是 MyBatis 与数据库交互的核心对象,负责执行 SQL 语句、获取 Mapper 接口实例、管理事务等。
Mapper 接口操作
  1. 获取 Mapper 接口实例
  • 通过 SqlSession 获取 Mapper 接口的实现类实例。MyBatis 会在运行时生成 Mapper 接口的实现类,并通过 SqlSession 获取其实例。
执行 SQL 语句
  1. 调用 Mapper 方法
  • 调用 Mapper 接口的方法,例如 mapper.selectUserById(1);
  1. 参数处理
  • 将方法参数转换为 SQL 语句中的占位符(如#{id})所需的值。MyBatis 使用 TypeHandler 将 Java 类型转换为 JDBC 类型。
  1. 执行 SQL 语句
  • MyBatis 使用 JDBC 通过数据源执行 SQL 语句,数据库返回结果集。
  1. 结果映射
  • 将 SQL 查询结果集映射为 Java 对象。根据映射文件或注解中的配置,将结果集中的数据映射到 Java 对象的属性中。
事务管理
  1. 提交事务
  • 如果执行的数据操作需要提交事务,调用 session.commit();提交事务,提交事务会将所有未提交的更改保存到数据库。
  1. 回滚事务
  • 如果操作过程中发生异常,需要回滚事务,调用 session.rollback();回滚事务,回滚事务会撤销所有未提交的更改。
关闭资源
  1. 关闭 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 中#{}、${}的区别

  1. #{}(预编译方式)
  • 安全性:#{} 采用了预编译(PreparedStatement)的方式,有效防止了 SQL 注入。因为 MyBatis 会将 #{} 中的内容当作参数处理,而不是直接拼接到 SQL 语句中。
  • 处理方式:MyBatis 会为 #{}中的内容生成一个参数占位符 ?,并使用 PreparedStatement 的 setXXX() 方法来设置参数值。因此,你不需要担心数据类型或引号问题。
  • 例子:

    • SELECT * FROM users WHERE id = #{userId},这条 SQL 语句在 MyBatis 中处理时,会将其转换为类似 SELECT * FROM users WHERE id = ?的形式,并通过 PreparedStatement 的 setInt() 或其他相关方法来设置 userId 的值。
  1. ${}(字符串拼接方式)
  • 安全性:${}是直接字符串拼接的方式,所以存在 SQL 注入的风险。如果参数来自用户输入或其他不可信的来源,那么使用 ${} 是非常危险的。
  • 处理方式:MyBatis 会直接将${} 中的内容替换到 SQL 语句中。这意味着你需要自己处理数据类型、引号等问题。
  • 用途:虽然 ${}存在安全风险,但在某些场景下它是必要的。例如,当你要动态地构建表名或列名时,就必须使用${}
  • 例子:SELECT * FROM ${tableName},这里的 tableName 是一个变量,MyBatis 会直接将其替换到 SQL 语句中。因此,如果你不能保证 tableName 的来源是可信的,那么这条 SQL 语句就存在 SQL 注入的风险。

4. Mybatis 自带连接池都有什么?

MyBatis 自带的连接池实现主要有两种:PooledDataSourceUnpooledDataSource。这两种连接池实现提供了不同的特性和使用场景。

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:一个功能丰富的连接池实现,适用于需要高级功能的场景。
  • 使用第三方连接池时,可以通过配置 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,备注:思否面试群。


王中阳讲编程
805 声望297 粉丝