| title | MyBatis 面试 | ||||
|---|---|---|---|---|---|
| date | 2025-04-29 23:33:26 -0700 | ||||
| order | 99 | ||||
| categories |
|
||||
| tags |
|
||||
| permalink | /pages/4dec9ccb/ |
MyBatis 中 #{} 和 ${} 的区别对比
| 特性 | #{}(预编译占位符) |
${}(字符串拼接) |
|---|---|---|
| 底层原理 | 使用 PreparedStatement,生成带 ? 的 SQL,预编译防止注入。 |
直接拼接字符串到 SQL 中,无参数化处理。 |
| SQL 注入风险 | ❌ 安全(自动转义特殊字符)。 | ✔️ 高风险(需手动过滤参数)。 |
| 适用场景 | 动态条件值(如 WHERE id = #{value})。 |
动态表名、列名(如 ORDER BY ${column})。 |
| 数据类型处理 | 自动识别 Java 类型,匹配 JDBC 类型(如 Date→TIMESTAMP)。 |
原样替换,可能导致语法错误(如字符串未加引号)。 |
| 性能 | 预编译 SQL 可复用,高效。 | 每次生成新 SQL,效率较低。 |
| 示例 | xml SELECT * FROM user WHERE name = #{name} |
xml SELECT * FROM ${tableName} |
关键结论
- 优先用
#{}:处理用户输入或条件值,确保安全。 - 谨慎用
${}:仅用于非用户输入的动态 SQL 部分(如动态表名),需手动过滤参数。 - 常见错误:
- 错误:
ORDER BY #{column}(预编译后引号包裹列名,语法错误)。 - 正确:
ORDER BY ${column} LIMIT #{limit}(混合使用)。
- 错误:
底层机制对比
#{} 的执行流程(安全)
-- 生成的 SQL(预编译)
SELECT * FROM user WHERE id = ?;
-- 参数值通过 PreparedStatement 安全传递
pstmt.setInt(1, 5);${} 的执行流程(风险)
-- 生成的 SQL(直接拼接)
SELECT * FROM user WHERE name = 'Alice' OR '1'='1'; -- 注入攻击示例何时必须使用 ${}?
-
动态表名/列名:
SELECT * FROM ${tableName} WHERE ${column} = #{value} -
SQL 函数或关键字:
ORDER BY ${sortField} ${sortOrder}
安全建议
- 使用
${}时,用@Param注解白名单校验:List<User> selectByTable(@Param("tableName") String tableName);
<!-- 手动校验表名合法性 --> SELECT * FROM ${tableName} WHERE 1=1 <if test="tableName in {'user', 'order'}"> AND status = #{status} </if>
| 对比维度 | MyBatis | Hibernate |
|---|---|---|
| SQL 灵活性 | 方便优化 SQL,灵活性高 | 自动生成 SQL,复杂查询需 HQL 或原生 SQL,灵活性较低 |
| 学习成本 | 需熟悉 SQL 和数据库特性,适合有 SQL 经验的团队。 | 面向对象思维,适合快速上手 ORM 的团队。 |
| 开发效率 | 需手动编写 SQL 和结果映射,适合定制化需求。 | 自动化 CRUD,快速开发简单应用。 |
| 缓存机制 | 提供一级/二级缓存,需手动管理。 | 内置多级缓存(查询缓存、集合缓存),自动化程度高。 |
| 数据库兼容性 | SQL 依赖具体数据库语法,移植性较差。 | 通过方言(Dialect)适配多数据库,移植性好。 |
| 关联映射 | 需手动配置 <association>/<collection>。 |
自动管理对象关系(如 @OneToMany),配置简洁。 |
| 适用场景 | 复杂查询、高性能系统(如金融、电商)。 | 快速开发、对象模型复杂的应用(如管理后台)。 |
总结选择建议
-
选择 MyBatis:
- 需要精细控制 SQL,追求极致性能。
- 项目涉及多表复杂查询或数据库特性优化。
-
选择 Hibernate:
- 快速开发,业务以简单 CRUD 为主。
- 团队熟悉 ORM,希望减少 SQL 编写。
混合使用:部分项目用 MyBatis 处理复杂查询,Hibernate 处理简单模块。
MyBatis 有以下核心组件:
SqlSessionFactoryBuilder:负责创建SqlSessionFactory实例。用完即弃。SqlSessionFactory:负责创建SqlSession实例。全局单例,配置中心。SqlSession:通过方法签名和Mapper相互映射。请求级核心,需及时关闭。Mapper:映射器是一些由用户创建的、绑定 SQL 语句的接口。轻量级对象,随用随建。
下面是它们之间的关系:
SqlSessionFactoryBuilder → SqlSessionFactory → SqlSession → Mapper Proxy
(方法级) (应用级) (请求级) (方法级)
- 生命周期:方法级(最短)
- 作用:用于构建
SqlSessionFactory,解析 XML 配置(如mybatis-config.xml)。 - 特点:
- 构建完成后即可销毁,无状态,不占用资源。
- 通常作为局部变量使用。
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);- 生命周期:应用级(最长)
- 作用:创建
SqlSession,全局唯一,线程安全。 - 特点:
- 通常作为单例存在于整个应用运行期间。
- 维护数据库连接池和全局配置(如缓存、别名)。
// 推荐通过单例管理
public class MyBatisUtil {
private static final SqlSessionFactory factory;
static {
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static SqlSessionFactory getFactory() {
return factory;
}
}- 生命周期:请求/事务级
- 作用:执行 SQL、获取 Mapper 接口实例、管理事务。
- 特点:
- 非线程安全,每次请求需创建新实例,用完后必须关闭(避免连接泄漏)。
- 默认不自动提交事务,需手动
commit()或rollback()。
try (SqlSession session = factory.openSession()) { // 自动关闭
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectById(1);
session.commit(); // 提交事务
}- 生命周期:方法级(与
SqlSession绑定) - 作用:通过动态代理将接口方法调用转换为 SQL 执行。
- 特点:
- 由
SqlSession创建,生命周期跟随SqlSession。 - 无需手动实现,MyBatis 自动生成代理类。
- 由
// 代理对象随 SqlSession 销毁而失效
UserMapper mapper = session.getMapper(UserMapper.class);MyBatis 的架构设计通过 分层解耦 和 动态代理 实现了 SQL 与 Java 代码的分离,其核心在于:
- 配置驱动:集中管理 SQL 和映射规则。
- 组件化:各层职责单一,易于扩展(如插件)。
- 平衡灵活与易用:既保留 JDBC 的掌控力,又简化了重复操作。
这种设计使其在需要高性能和灵活 SQL 的场景中表现优异,尤其适合中大型复杂业务系统。
MyBatis 的架构设计以 SQL 与 Java 对象的灵活映射 为核心,采用分层模块化设计,平衡了灵活性与易用性。
分层架构设计
MyBatis 的架构分为四层,各层职责明确,通过接口解耦:
| 层级 | 核心组件 | 职责 |
|---|---|---|
| 接口层 | SqlSession、Mapper 接口 |
提供开发者使用的 API(如 selectOne、insert),屏蔽底层实现细节。 |
| 核心处理层 | Executor、StatementHandler |
执行 SQL 语句、处理参数绑定和结果映射,实现插件拦截链。 |
| 基础支撑层 | DataSource、Transaction |
管理数据库连接池、事务,提供类型转换(TypeHandler)和缓存支持。 |
| 扩展层 | Interceptor(插件) |
通过动态代理拦截核心组件,实现功能扩展(如分页、性能监控)。 |
功能:为上层提供通用能力支持
- 类型处理器 (TypeHandler):处理 Java 类型与 JDBC 类型转换(如
String↔VARCHAR)。支持自定义扩展(如枚举类型转换)。 - 连接管理:集成连接池(如 HikariCP、Druid),管理数据库连接。
- 事务管理:提供 JDBC 和 Managed 两种事务模式(可集成 Spring 事务)。
- 缓存管理:一级缓存(
SqlSession级别)、二级缓存(Mapper级别)。支持 Redis、Ehcache 等第三方缓存集成。
功能:执行 SQL 并处理结果映射
- 配置解析 (Configuration):加载
mybatis-config.xml和Mapper.xml,存储所有配置信息(如别名、插件)。 - SQL 解析 (SqlSource & BoundSql):解析动态 SQL(
<if>、<foreach>),生成可执行的 SQL 字符串和参数映射。 - 执行器 (Executor)
- 类型:
SimpleExecutor:默认执行器,每次执行新开PreparedStatement。ReuseExecutor:复用Statement对象。BatchExecutor:批量操作优化。
- 职责:调用 JDBC 执行 SQL,触发插件拦截链。
- 类型:
- 结果集处理 (ResultSetHandler):将
ResultSet转换为 Java 对象(根据ResultMap或自动映射)。


