哈喽,大家好,我是小米,一个 31 岁、在 Java 世界里摸爬滚打十年的老程序员大哥哥。今天想和大家聊聊我最近面试中遇到的一道高频题——
“MyBatis 为什么需要预编译?”
说起来,这是一道我以为很基础,结果差点翻车的面试题……不卖关子了,我们进入正题。
故事的开始:一次“看似简单”的社招面试
最近因为老东家转型,我打算跳槽看看新的机会,于是投了几家公司,拿到了几个社招面试。某互联网公司,技术面二面,一个典型的老派面试官,戴眼镜、沉稳有礼,一看就是经验丰富的“代码艺术家”。
面试到中段,他突然问我一句:
“你用 MyBatis 开发过哪些项目?”
我想了想,说我最近三年一直用 Spring Boot + MyBatis 开发企业管理系统,熟悉 XML 映射、注解方式、分页插件、自定义拦截器啥的。
他说,“不错,那你能说说 MyBatis 为什么需要预编译吗?如果不预编译会有什么后果?”
我愣了一下。
这题,我知道是 SQL 的预编译,但突然这么问,还是让我有点懵。
我调整了下状态,说:“因为预编译可以防止 SQL 注入……还能提高性能……”
他说:“很好,那具体是怎么实现的?MyBatis 是怎么用预编译的?和 JDBC 的 PreparedStatement 是一个意思吗?”
这一刻,我感觉我的脑袋里有个“滴”地一声——滴水穿石也得有个方向吧!
于是我从头开始,给他讲了一个 “预编译和 MyBatis 的爱恨情仇” 的故事。
什么是预编译?PreparedStatement 是老朋友
我们得先搞清楚“预编译”到底是什么。
当我们用 JDBC 写原始 SQL 时,有两种选择:
vs.
第二种就是我们说的“预编译”。在这种方式下,SQL 语句会提前编译好,数据库把结构部分提前处理,只等你把参数一塞,它就能直接跑起来。
而且重点是,参数是“值”,不是“拼接” 。
于是,SQL 注入被防了,执行计划也能被缓存,性能提升也就自然来了。
那 MyBatis 怎么用预编译的?
MyBatis 不造轮子,它的底层其实就是封装了 JDBC 的 PreparedStatement。
当你写这样一个 Mapper 接口:
或者 XML 里这样写:
MyBatis 会把 #{name} 解析成一个“问号占位符 ? ”,并通过底层的 JDBC PreparedStatement 来进行参数设置:
所以你看到的 #{},其实就是一个语法糖,它最终会变成 ?,然后安全地填值进去。
这就是 MyBatis 中所谓的“预编译”。
如果不预编译会怎样?小米讲段“血泪史”
说到这里,我插了个小故事。
我当年刚入行那会儿,在一个外包项目里搞了个“拼接 SQL”的神器,用 Statement 拼接了一段“自由组合搜索语句”。
当用户输入用户名时,SQL 是这样:
结果有个哥们不怀好意,在输入框输入了:
于是,SQL 变成了:
完蛋!所有用户数据被查出来了!这就是最常见的 SQL 注入 攻击。
于是我意识到,拼接 SQL 是万恶之源。而 PreparedStatement + #{} 的方式,能安全、优雅地避免这个坑。
我和面试官说完这段后,他点头说,“不错,那你知道 MyBatis 的 ${} 和 #{} 有啥区别吗?”
我毫不犹豫:“${} 是字符串替换,真的会被拼接进 SQL,是不安全的;#{} 是值替换,会使用预编译,是安全的。”
性能提升是预编译的另一重意义
聊完安全性,咱们来谈谈性能。这其实是很多面试官爱追问的一点。
1. 预编译提升执行效率
数据库执行一条 SQL 的流程分三步:
- 解析 SQL 语句
- 生成执行计划
- 执行
如果你每次执行的 SQL 结构一样,只是参数不同,那么用预编译(PreparedStatement)可以让数据库复用“执行计划”。
这样做的好处就是——
省下了反复编译 SQL 的时间,执行效率自然提升。
2. MyBatis 的执行器缓存机制
其实 MyBatis 还有个小心思。
在默认的 Executor 实现中(比如 ReuseExecutor、BatchExecutor),它会缓存一些 MappedStatement 和 SQL 的执行计划对象,避免每次都走创建流程。
换句话说,如果你写得规范,用得合理,MyBatis 本身就是一个“执行计划复用高手”。
总结复盘:为什么 MyBatis 需要预编译?
面试到了尾声,我做了个收尾总结,分享给你们:
- 防止 SQL 注入: 使用 #{} 会被替换为 ? 占位符,底层使用 PreparedStatement,防止注入攻击;
- 提高执行效率: 数据库可以复用执行计划,提升性能;
- 降低系统资源消耗: 避免频繁解析 SQL,降低数据库压力;
- MyBatis 本身设计理念: 遵循 JDBC 最佳实践,将 SQL 与参数分离,提高可维护性和可读性;
注意事项: {},但参数永远别这么搞!
后记:这次面试我拿到了 Offer
这场面试过后,我收到了 offer,还被点名夸我“基础扎实”。
其实很多时候,面试问的不是多高级的算法,而是你对日常开发工具的理解是否 “知其然,知其所以然” 。
MyBatis 是我们日常开发中离不开的 ORM 工具,它的每一个设计,其实都不是“拍脑袋”决定的,背后有深厚的底层逻辑和工程经验。
结语:别让工具变成“黑箱”,你得懂它在干嘛!
写代码就像开车,工具是方向盘、发动机、刹车。如果你只知道踩油门,万一前面是个坑怎么办?
了解工具底层逻辑,是从“写代码”走向“写好代码”的必经之路。
END
今天的分享到这里,希望你在下一次面试遇到类似问题时,也能像我一样,胸有成竹地讲出背后的故事!
如果你喜欢这样的面试题解析风格,别忘了点个“赞”或者“在看”,你们的支持,是我继续写作的动力!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!