MyBatis基础教程:Java持久层框架入门

掘金夜猫子
2025-06-29 16:43
阅读 215

MyBatis 基础实战:从 JDBC 到 ORM 的跨越之路

MyBatis 基础实战:从 JDBC 到 ORM 的跨越之路

开篇:为什么选择 MyBatis?

我是一名有 5 年后端开发经验的 Java 工程师,经历过多个中小型项目的迭代与重构。在刚开始工作的那一年,我和很多新人一样,都是直接写 JDBC 操作数据库的代码。后来接触到了 Hibernate 这样的全自动化 ORM 框架,当时觉得“哇,终于不用自己拼 SQL 了!”但没过多久就发现,在实际项目中,Hibernate 的灵活性和性能表现并不总是那么令人满意。

真正让我转变观念的是第一次接触 MyBatis。那时候团队要做一个数据中台服务,核心是打通几个业务系统的数据接口,要求高效、灵活地操作数据库。起初我还抱着 Hibernate 不放,结果在多表关联、复杂查询、分页优化等场景下频繁碰壁。最终我们选择了 MyBatis 作为持久层框架,没想到这一用就是这几年来最顺手的一个技术选型。

今天这篇文章想结合我个人使用 MyBatis 的真实经历,给大家分享一下这个框架的基础入门心得,也会穿插一些我踩过的坑和总结的经验。


项目背景:一个典型的中台系统需求

我们当时的项目是一个公司级的数据汇聚平台,主要职责是把 CRM、ERP、WMS 等系统的部分核心数据汇总起来,对外暴露统一的 RESTful 接口供前端和其他后台调用。

数据结构方面比较复杂,涉及大量的 JOIN 查询和跨库映射。同时由于是面向多个系统的聚合查询,对性能要求也很高,不能接受那种“一句查询执行好几秒”的情况。


遇到的挑战:传统方式 vs ORM 的尴尬

初期尝试用纯 JDBC 去实现数据访问层,结果很快出现了以下问题:

  • SQL 分散在各个 DAO 类中,可维护性差;
  • 手动处理 ResultSet 映射繁琐,容易出错;
  • 多条件组合查询时,SQL 字符串拼接难以管理;
  • 数据源连接池、事务控制都需要自己封装,容易出现并发问题。

切换到 Hibernate 后虽然简化了很多 CRUD 操作,但也暴露出新问题:

  • 自动生成的 SQL 性能不佳,特别是 LEFT JOIN 场景;
  • 对于动态查询支持较弱,需要借助 Criteria API 写法不够直观;
  • 有些复杂报表查询无法通过实体类模型表达;
  • 联合主键和非规范化设计的表结构导致映射困难。

这时,我们开始研究其他替代方案,MyBatis 就是在这个背景下进入视野的。


解决思路:MyBatis 的“中间派”优势

MyBatis 是介于原生 JDBC 和全自动 ORM(如 Hibernate/JPA)之间的半自动化 ORM 框架。它不强制你完全使用对象模型来映射数据库表,而是保留了 SQL 的自由度,同时又屏蔽了底层操作,比如连接获取、参数绑定、结果集处理、事务管理等。

我们选择 MyBatis 主要是看中了它的以下几个特点:

  • SQL 完全掌控:你可以自己写 SQL,适合复杂业务场景;
  • 轻量级配置:比起 Spring Data JPA,MyBatis 上手更快;
  • 灵活的映射机制:可以通过 XML 或注解定义 SQL 和结果集映射;
  • 良好的社区生态:Spring Boot 支持非常成熟,集成简单;
  • 易于调试与优化:可以直接看到发出的 SQL,方便 DBA 协助调优。

数据库设计模型-1


代码实践:快速上手 MyBatis

为了让大家有个直观的认识,下面是一段简单的示例代码。

添加依赖(以 Maven 为例)

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
</dependencies>

微服务架构示意图-2

配置数据源(application.yml)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.model

编写 Mapper 接口

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(Long id);
    
    List<User> selectByCondition(@Param("name") String name, @Param("age") Integer age);
}

对应的 UserMapper.xml 文件如下:

<?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="com.example.mapper.UserMapper">
    
    <select id="selectByCondition" resultType="com.example.model.User">
        SELECT * FROM users
        <where>
            <if test="name != null">
                AND name LIKE CONCAT('%', #{name}, '%')
            </if>
            <if test="age != null">
                AND age >= #{age}
            </if>
        </where>
    </select>
    
</mapper>

是不是感觉比 Hibernate 更加“接地气”?尤其是 <where><if> 标签的配合使用,让动态 SQL 清晰又可控。


踩坑经验:那些年我们一起绕过的弯路

1. 忘记开启自动提交或事务回滚

有一次在一个批量导入逻辑中,插入几千条记录失败后没有进行回滚,结果造成脏数据。后来我们在 Service 层添加了 @Transactional 注解,并且显式设置了事务边界。

@Transactional
public void importData(List<User> userList) {
    for (User user : userList) {
        userMapper.insert(user);
    }
}

2. 结果集映射错误导致字段丢失

早期经常因为字段名和属性名不一致导致某些字段始终为 null。后来在 resultMap 中手动指定映射关系,或者直接开启驼峰命名转下划线自动转换功能:

mybatis:
  configuration:
    mapUnderscoreToCamelCase: true

3. 误用 $ 而不是 #,引发 SQL 注入风险

这是一个很经典的坑。例如:

-- 错误用法
SELECT * FROM users WHERE name = '${name}'

-- 正确做法
SELECT * FROM users WHERE name = #{name}

#{} 会预编译防止注入,${} 是字符串替换,容易被攻击。


效果总结:项目上线后的变化

引入 MyBatis 后,整个项目的数据库操作模块更加清晰,接口响应速度平均提升了 30% 以上。特别是在一些复杂的 JOIN 查询、报表统计场景中,可以自由编写 SQL,避免了 Hibernate 生成低效语句的问题。

同时,SQL 与 Java 代码分离的设计也便于 DBA 协助优化查询语句。我们在后期还结合了 Druid 监控,实时查看慢 SQL,进一步提升了系统稳定性。


我的经验建议

  1. 不要迷信 ORM 框架,要懂得权衡
    Hibernate 很强大,但在复杂查询面前显得力不从心。而 MyBatis 提供了一种平衡的选择。

  2. SQL 没有想象中那么简单,要学会写优雅的 SQL
    特别是面对大数据量时,索引、JOIN 顺序、子查询等都会影响性能。有时候优化一条 SQL,胜过十次 Java 层面的优化。

  3. 合理使用动态 SQL 和 resultMap
    动态 SQL 是 MyBatis 的灵魂之一,学会灵活运用 <if><choose><set> 等标签,可以大大减少代码冗余。

  4. 搭配日志工具追踪 SQL,提升 Debug 效率
    推荐使用 MyBatis Log Plugin 插件,在 IDE 里就可以直接看到拼好的 SQL,极大方便调试。

  5. 生产环境务必做好监控和调优
    比如使用 Druid、SkyWalking 等工具对 SQL 执行时间、连接池状态进行监控,及时发现问题。


写在最后:适合自己的才是最好的

MyBatis 不是万能的,也不是唯一的选择。在我参与的微服务架构项目中,也遇到过使用 JPA + QueryDSL 的团队,他们的做法更适合严格的领域模型驱动开发。但我相信在大多数企业级 Java 后端项目中,MyBatis 依然有着不可替代的优势。

作为一个开发者,我觉得最重要的是理解每一项技术背后的原理和适用场景。不要盲目追求“最新最热”,也不要一成不变地守着老一套。很多时候,选型没有绝对正确,只有更合适。

希望这篇结合了我个人实战经验的文章,能够帮助大家更好地理解和使用 MyBatis。如果你也有类似的踩坑经历或者不同看法,欢迎留言交流,共同成长!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝