从“写烂了的 DAO”到优雅操作数据库:我在项目中踩出的 MyBatis 入门心得
作为一名在互联网公司干后端开发多年的老兵,我深知一个持久层框架对系统开发效率和稳定性的影响有多大。今天想和大家聊聊我在一个实际项目中使用 MyBatis 的入门经历,以及在这个过程中遇到的一些坑和感悟。
希望通过这篇文章,能帮你在学习或使用 MyBatis 时少走点弯路,或者至少知道怎么绕过那些隐藏得很深的大坑。
背景:新需求催生技术选型

事情要从去年我们部门接到一个新的运营平台项目说起。这个项目主要是为了帮助市场团队做活动管理、用户权益发放、数据报表展示等日常运营支持功能。
我们团队原本是 Spring Boot + Hibernate 的坚定使用者,但这次的需求里有很多查询非常复杂,涉及到多表联合、动态筛选、分页排序等等。传统的 Hibernate 对这些场景的支持略显笨重,而且有时候生成的 SQL 过于啰嗦,也不太好调试。
于是,在一次项目讨论会上,我提议尝试一下 MyBatis,毕竟之前在开源社区里听说它的灵活性更高,尤其是对于需要频繁调整 SQL 的业务场景来说,优势明显。
最终团队决定采用 Spring Boot + MyBatis 的组合,我也顺势接下了这块的技术攻坚任务。
我们遇到了哪些问题?

刚引入 MyBatis 后,确实有一段适应期,特别是在以下几个方面:
1. 数据库字段与实体类不一致带来的映射难题
我们之前的 ORM 框架基本都是自动做映射的,但现在发现,MyBatis 默认并不会处理像 user_id → userId 这样的命名转换,导致大量手动写 ResultMap,效率很低。
public class User {
private Long userId;
private String userName;
}
对应的数据表字段却是:
CREATE TABLE user (
id BIGINT PRIMARY KEY,
name VARCHAR(50)
);
如果不配置映射,那查出来就是空值!
2. 动态查询语句拼接混乱
比如我们要根据用户名、注册时间范围等多个条件查询用户,传统做法可能是用 if-else 拼接字符串,SQL 安全性和可读性都差到爆。虽然 Hibernate 有 Criteria API,但我们在换 MyBatis 后就失去了这些便利。
我们需要一种方式,既能灵活构建查询语句,又能避免 SQL 注入风险。
3. 编码风格不统一,多人协作容易出错
由于没有现成的标准模板,不同同事写的 Mapper 文件五花八门,有的还直接把 SQL 写在 XML 里,有的则用上了注解,还有人写了原生 JDBC……后期维护成本极高。
这些问题让我意识到,光会用 MyBatis 是不够的,必须从架构设计层面去规范它,让它真正变成生产力工具,而不是负担。
解决思路和架构设计


针对以上问题,我做了几个关键决策:
一、统一数据库命名规范 + 自动化映射
我们制定了一套新的数据库命名规范,将字段全部转为下划线风格(如 user_name、created_at),然后通过全局配置让 MyBatis 支持自动映射。
# application.yml 配置
mybatis:
configuration:
mapUnderscoreToCamelCase: true
这样就能实现自动映射,省去了很多重复写 ResultMap 的麻烦。
二、引入 MyBatis 动态 SQL 组件
我们开始全面使用 <if>、<choose>、<where> 等标签来构建动态 SQL 查询,避免手写拼接。举个例子:
<!-- UserMapper.xml -->
<select id="selectUsers" parameterType="map" resultType="User">
SELECT * FROM user
<where>
<if test="userName != null and userName != ''">
AND name LIKE CONCAT('%', #{userName}, '%')
</if>
<if test="minCreateTime != null">
AND create_time >= #{minCreateTime}
</if>
<if test="maxCreateTime != null">
AND create_time <= #{maxCreateTime}
</if>
</where>
</select>
这段 XML 最终会被 MyBatis 渲染成结构清晰的 SQL,避免手动拼接的风险和 bug。
三、规范化 Mapper 层设计
我们制定了如下几条编码规范:
- 所有 SQL 必须写在 XML 文件中,禁止直接写在注解里(方便统一管理和调试)
- XML 文件必须命名为
<Entity>Mapper.xml,放在统一资源路径/resources/mappers - Mapper 接口使用标准命名规则:
<Entity>Mapper.java - 查询接口返回类型尽可能用实体类(POJO)或 DTO,避免使用 Map
- 复杂更新逻辑应结合事务管理器(@Transactional)
这样一来,整个项目的数据库层结构变得非常清晰,新成员上手也更容易。
核心代码片段示例
以下是我整理的几个 MyBatis 开发中的核心代码模板,供你参考。
1. 实体类定义(简洁明了)
public class Activity {
private Long id;
private String name;
private LocalDateTime startTime;
private LocalDateTime endTime;
private Integer status;
}
2. Mapper 接口定义
@Mapper
public interface ActivityMapper {
List<Activity> selectActivities(@Param("name") String name,
@Param("status") Integer status);
int insert(Activity activity);
}
3. XML 中的动态查询 SQL
<select id="selectActivities" resultType="Activity">
SELECT * FROM activity
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
4. 插入数据的示例
<insert id="insert">
INSERT INTO activity (name, start_time, end_time, status)
VALUES (
#{name},
#{startTime, jdbcType=TIMESTAMP},
#{endTime, jdbcType=TIMESTAMP},
#{status}
)
</insert>
这里注意日期类型的字段要指定 jdbcType=TIMESTAMP,否则某些数据库可能插入失败。
我踩过的那些坑
当然啦,刚开始使用 MyBatis 时我也踩了不少坑,下面分享几个比较典型的。
坑1:参数绑定错误,提示找不到属性名
一开始我写了个这样的接口:
List<User> findUsers(String keyword, LocalDateTime minTime);
然后在 XML 里这么调用:
<if test="keyword != null">AND name LIKE '%${keyword}%'</if>
结果一直报错:Parameter 'keyword' not found.
后来才知道,当接口方法有多个参数时,需要用 @Param 注解明确指定每个参数的名称,不然 MyBatis 不知道怎么映射。
正确的写法应该是:
List<User> findUsers(@Param("keyword") String keyword,
@Param("minTime") LocalDateTime minTime);
坑2:XML 文件没被加载,SQL 报错找不到方法
这个问题一般发生在集成阶段,尤其是在打包部署之后。原因是 MyBatis 的 mapper 文件没有正确配置扫描路径。
解决方案是在配置文件中加上:
mybatis:
mapper-locations: classpath:mapper/**/*.xml
确保项目启动时能正确加载所有的 XML 映射文件。
坑3:LIKE 查询出现 SQL 注入风险
最开始我直接用了 ${} 来拼接模糊查询:
AND name LIKE '%${keyword}%'
这是很危险的做法,可能会导致 SQL 注入攻击。
改成使用预编译 #{},并配合 CONCAT:
AND name LIKE CONCAT('%', #{keyword}, '%')
这样安全性高很多,也能防止恶意输入破坏查询逻辑。
实际效果与收益总结
经过几个月的实战打磨,这套基于 MyBatis 的持久层方案已经在我们项目中稳定运行,并取得了不错的效果:
| 指标 | 效果描述 |
|---|---|
| 查询效率 | 复杂 SQL 编写更加直观,优化空间更大,整体响应时间下降约 30% |
| 团队协作 | 统一了编码规范后,新人上手速度快了很多,代码 Review 更加高效 |
| 可维护性 | SQL 与 Java 逻辑分离,便于后续 DBA 或运维人员介入排查和优化 |
| 扩展能力 | 支持多种数据库方言,兼容性良好,后续可以轻松迁移至 Oracle、PostgreSQL 等 |
更关键的是,它让我们摆脱了过去 Hibernate 在复杂查询上的局限,同时又不像直接裸写 JDBC 那样繁琐。
几点经验建议送给正在入门 MyBatis 的你
如果你现在刚开始接触 MyBatis,或者准备在项目中使用它,这里有几点我亲测有效的小建议,希望能帮你避开一些陷阱:
✅ 1. 别一开始就搞太复杂的插件,先掌握基础用法
很多人一上来就想装 PageHelper、Dynamic-Datasource 之类的扩展,结果连基本的 XML 配置都没理清楚。建议先把 Mapper 接口 + XML 文件 + 参数映射 搞明白,再逐步深入。
✅ 2. 多用日志输出 SQL,方便定位问题
Spring Boot + MyBatis 可以通过日志级别设置,把执行的 SQL 输出出来:
logging:
level:
com.example.mapper: debug
这样就可以看到真实执行的 SQL 是什么样子的,调试时非常实用。
✅ 3. 尽量避免使用 ${},除非你知道自己在做什么
${} 是直接拼接 SQL 字符串,有安全隐患。而 #{} 是占位符,更安全也更适合大多数场景。
✅ 4. 使用 IDE 插件提升开发效率
推荐安装 MyBatisX 这个 IntelliJ IDEA 插件,它可以自动跳转 Mapper 接口与 XML 方法,写完 SQL 还能检查语法是否正确,非常好用。
✅ 5. 分离业务逻辑与数据访问层
MyBatis 是个持久层框架,不是业务逻辑中心。建议把具体的业务逻辑封装在 Service 层,保持 Mapper 接口职责单一,仅负责 CRUD 和简单查询。
结语:MyBatis 不是万能的,但它足够实用
回顾整个项目过程,我深刻体会到一点:选择一个适合当前业务场景的工具比盲目追求新技术更重要。
MyBatis 并不像 Hibernate 那样全自动,但在我们这种需要频繁调整 SQL、重视性能优化的项目中,它展现出了独特的优势。
希望这篇文章对你理解 MyBatis 有所帮助,也能让你在使用过程中少走弯路。如果你有任何关于 MyBatis 的问题,欢迎留言交流,我会尽我所能帮忙解答。
最后送大家一句我在项目中常常用来提醒自己的话:代码是用来给人看的,偶尔给机器跑跑。别忘了写得优雅一点,别让以后的自己看不懂今天的 SQL 😄
作者:某互联网大厂后端开发一枚,常年混迹于 Java 生态圈,热爱开源、热爱分享。欢迎关注我的公众号【Java修行手记】获取更多实战干货。

评论 0