MyBatis基础教程:Java持久层框架入门
引言:为什么选择MyBatis?

在我从事后端开发的这几年里,接触过不少持久层框架。从传统的JDBC到Hibernate再到如今主流的Spring Data JPA,每种技术都有其适用场景。然而在实际项目中,尤其是在需要对SQL有较强控制能力的业务系统里,MyBatis 始终占据一席之地。
记得我刚加入当前公司时,负责维护一个老项目,它用的就是 MyBatis。当时我对这个“介于ORM和原始SQL之间的桥梁”还很陌生。随着项目的深入,我发现,虽然它不像JPA那样开箱即用,但它的灵活性和性能优势在高并发、复杂查询的场景下真的特别香!
今天,我想结合自己的实际工作经验,来写一篇接地气的 MyBatis基础教程,希望能帮助刚刚入坑的朋友少走一些弯路。
项目背景:电商系统的订单模块重构

去年年底我们团队接手了一个电商系统的重构任务,其中有一个重点模块是订单管理。原系统使用的是纯 JDBC + DAO 模式,代码冗长且难以维护,尤其是面对多表联合查询、动态条件查询时,经常需要拼接大量 SQL 字符串,非常容易出错。
举个真实案例:
// 旧版伪代码
public List<Order> findOrders(String status, String keyword) {
StringBuilder sql = new StringBuilder("SELECT * FROM orders WHERE 1=1");
if (status != null) {
sql.append(" AND status='").append(status).append("'");
}
if (keyword != null) {
sql.append(" AND order_no LIKE '%").append(keyword).append("%'");
}
// 后续执行query...
}
这段代码看着简单,但在生产环境中动不动就是几十行拼接逻辑,稍不注意就会 SQL 注入或者字段拼错了。而且随着功能越来越多,DAO 层越来越臃肿,根本不敢轻易改动。
于是我们决定引入 MyBatis 来重构整个订单模块。
遇到的问题与挑战

挑战一:如何平滑迁移已有业务?
由于是重构项目,不能中断线上服务,必须采用渐进式替换的方式,逐步将原有 DAO 方法迁移到 MyBatis 中。这就要求新旧两套体系要能并存一段时间。
挑战二:复杂的业务查询条件
订单筛选涉及多个字段组合,比如时间范围、订单状态、用户ID、支付方式等,同时还支持分页查询,这对我们构造动态 SQL 提出了较高要求。
挑战三:数据库事务一致性
订单相关的操作往往需要更新多个表,如订单主表、订单明细表、库存表等,必须保证事务的一致性。而MyBatis本身并不处理事务管理,必须配合 Spring 才能实现。
解决思路与技术方案
针对上述问题,我们的解决思路如下:
- 使用 MyBatis 作为持久层,配合 Spring Boot 整合事务管理;
- 将原有的 DAO 接口封装为 Mapper,并利用 MyBatis 的注解或 XML 管理 SQL;
- 利用 MyBatis 动态 SQL 特性,统一构建灵活的查询语句;
- 逐步替换原有方法,确保每个接口的功能一致性后再上线。
以下是我们的核心依赖配置(pom.xml):
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
核心代码实践
示例一:Mapper 接口定义
我们以订单查询为例,定义一个 OrderMapper 接口:
@Mapper
public interface OrderMapper {
@Select("SELECT * FROM orders WHERE id = #{id}")
Order selectById(Long id);
List<Order> selectByCondition(@Param("condition") OrderQuery condition);
}
示例二:XML 中的动态查询
为了应对复杂的查询条件,我们在 XML 文件中编写 SQL:
<!-- OrderMapper.xml -->
<select id="selectByCondition" resultType="Order">
SELECT *
FROM orders
<where>
<if test="condition.status != null and condition.status != ''">
AND status = #{condition.status}
</if>
<if test="condition.startTime != null">
AND create_time >= #{condition.startTime}
</if>
<if test="condition.endTime != null">
AND create_time <= #{condition.endTime}
</if>
<if test="condition.userId != null">
AND user_id = #{condition.userId}
</if>
</where>
ORDER BY create_time DESC
</select>
这样可以非常优雅地处理各种参数组合,同时也避免了字符串拼接的风险。
示例三:事务控制
我们通过 Spring 的事务管理器来控制事务:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void updateOrderStatus(Long orderId, String newStatus) {
Order order = orderMapper.selectById(orderId);
order.setStatus(newStatus);
orderMapper.updateOrder(order);
// 更新关联的明细表或者其他表,此处略去具体逻辑
}
}
只要加了 @Transactional 注解,Spring 就会自动开启事务并处理回滚,大大简化了事务控制的复杂度。
踩坑经验分享
坑点一:XML 中的标签嵌套错误
有时候 <if> 标签没写对,特别是没有闭合,会导致 XML 解析失败。比如:
<if test="xxx">AND a=1</if> <!-- 没写结束标签 -->
这个问题刚开始查了很久才定位到,建议大家一定要注意格式规范,最好使用 IDE 插件辅助校验。
坑点二:参数绑定异常
比如传入了一个对象,但是在 XML 中直接用了 #{userId},而不是 #{condition.userId},就会提示找不到属性。
解决方案:使用 @Param 注解显式命名参数。
坑点三:事务失效
如果事务方法被同一个类中的非事务方法调用,事务是不会生效的。原因是你调用的是 this 对象的方法,Spring AOP 无法拦截。这种情况需要用 AopContext.currentProxy() 或者重新设计结构。
实施效果与收益
完成重构后,整个订单模块的可维护性和性能都有了明显提升:
- 代码更简洁:原来的几十行拼接 SQL 被替换成了清晰的 XML 查询;
- 扩展性更强:新增查询字段只需在 XML 和 Query 类中添加判断即可;
- 性能提升:MyBatis 更加贴近数据库操作,减少了不必要的 ORM 映射损耗;
- 故障排查更快:日志可以直接打印出实际执行的 SQL,方便调试。
上线后观察了一段时间,QPS 提升了约 20%,GC 压力也有所下降。
经验总结与建议
如果你也在考虑引入 MyBatis,以下几点是我踩坑之后总结的经验:
合理使用动态 SQL
不要贪图省事把所有逻辑塞进<if>里面,适当拆分 SQL 可以提高可读性和维护效率。实体类和映射文件保持一致
尤其是字段名和数据库列名要对应好,可以通过mapUnderscoreToCamelCase自动转换,也可以手动定义resultMap。善用日志插件(如 Log4j2、MyBatis-Plus 提供的日志)
开发阶段务必打开 SQL 日志输出,有助于快速发现执行异常或慢查询。谨慎对待事务粒度
太粗的事务容易导致锁竞争,太细又可能导致数据不一致,根据业务需求权衡使用。结合 MyBatis-Plus 可以进一步提效
如果你的项目不需要太多自定义 SQL,可以考虑用 MyBatis-Plus 替代原生 MyBatis,内置了很多常见 CRUD 方法。
写在最后
回顾这次 MyBatis 的应用实践,我觉得它是一个非常适合 Java 后端工程师掌握的基础技能。相比 Hibernate/JPA 的“全自动”风格,MyBatis 的“半自动”让我们在 SQL 上拥有更高的自由度,尤其适合中大型项目中对性能和灵活性有双重要求的场景。
作为一个过来人,我也走过不少弯路。希望这篇文章能够帮你绕过一部分坑,在学习 MyBatis 的路上走得更稳一点。
如果你正在学习或者准备尝试 MyBatis,欢迎留言交流,我们可以一起探讨更多实战技巧!

评论 0