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

JSON搬运员
2025-12-17 20:03
阅读 1922

大家好,我是小林,一个刚从成都某大专毕业的前端自学选手。没错,你没看错——我主攻前端,但最近被公司“安排”去搞后端,理由是“全栈工程师更吃香”。虽然内心一万只草泥马奔腾而过,但为了月底那点工资(和不被裁的KPI),还是硬着头皮啃起了 Java 后端技术栈。

上周五晚上,产品又提了个“小需求”:要加个用户行为日志统计模块,得存到 MySQL 里。我一脸懵:前端发个请求完事了?结果后端老哥甩给我一句:“你不是会写代码吗?用 MyBatis 接一下。” 好家伙,我连 pom.xml 是啥都快忘了,还得现学 MyBatis。

于是就有了这篇新手友好型 MyBatis 入门教程。不吹牛,纯实战踩坑总结,适合像我这种半路出家、Vim 党、讨厌 IDE 自动补全的“野生程序员”。


为啥选 MyBatis?而不是 JPA 或者手写 JDBC?

先说背景。我们公司是个中小型 SaaS 产品团队,后端主要用 Spring Boot + MyBatis 的组合。为啥不用 Spring Data JPA?产品经理说:“我们要灵活查数据,有些 SQL 得手写优化。” 运维大哥也补刀:“JPA 那个 N+1 查询问题,线上炸过一次,别碰。”

MyBatis 的核心思想就俩字:SQL 和对象解耦。它不像 JPA 那样全自动映射,而是让你手写 SQL,然后通过 XML 或注解把结果映射成 Java 对象。对前端转后端的我来说,反而更容易理解——毕竟 SQL 我还能看懂,ORM 黑盒直接劝退。

而且,MyBatis 在国内企业级应用里真·主流。你看那些大厂招聘 JD,十个有八个写着“熟悉 MyBatis”,比 Redis 还常见(手动狗头)。


上手第一步:项目搭建(别被 Maven 劝退)

我用的是 Spring Boot 2.7 + MyBatis 3.5 + MySQL 8。Maven 配置如下(别问为啥不用 Gradle,我们组统一 Maven,说是为了“兼容老项目”):

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

application.yml 里配数据库:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

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

重点来了:mapper-locations 指向你的 XML 文件目录,type-aliases-package 让你在 XML 里不用写全限定类名,省事!


写个最简单的 CRUD:用户表操作

假设我们有个 user 表:

CREATE TABLE user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    email VARCHAR(100)
);

对应的 Java 实体类:

public class User {
    private Long id;
    private String name;
    private String email;
    // getter/setter 省略(其实我用 Lombok,但领导不让,说“调试时看不到字段”)
}

Mapper 接口 + XML 配置

MyBatis 最经典的玩法就是 接口 + XML。先写接口:

@Mapper
public interface UserMapper {
    List<User> findAll();
    User findById(Long id);
    void insert(User user);
    void update(User user);
    void deleteById(Long id);
}

然后在 src/main/resources/mapper/UserMapper.xml 里写 SQL:

<?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.myapp.mapper.UserMapper">
    <select id="findAll" resultType="User">
        SELECT * FROM user
    </select>

    <select id="findById" parameterType="long" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user(name, email) VALUES(#{name}, #{email})
    </insert>

    <update id="update" parameterType="User">
        UPDATE user SET name=#{name}, email=#{email} WHERE id=#{id}
    </update>

    <delete id="deleteById" parameterType="long">
        DELETE FROM user WHERE id = #{id}
    </delete>
</mapper>

几个关键点:

  • namespace 必须和接口全路径一致
  • resultType="User" 能用是因为前面配了 type-aliases-package
  • useGeneratedKeys="true" 是为了拿到自增主键,不然 user.getId() 永远是 null(我第一次就栽这儿,测了半小时以为代码错了)

踩坑实录:那些让我想砸键盘的瞬间

  1. XML 文件没被扫描到
    报错:Invalid bound statement (not found)
    原因:mapper-locations 路径写错,或者 XML 文件没放在 resources 下。记住:Maven 默认只编译 src/main/resources,别把 XML 放 java 目录里!

  2. 下划线转驼峰失效
    数据库字段是 create_time,Java 属性是 createTime,结果查出来是 null。
    解决:在 application.yml 加一行:

    mybatis:
      configuration:
        map-underscore-to-camel-case: true
    
  3. 事务不生效
    我在 Service 里调了两个 insert,结果第二个失败了,第一个却提交了!
    原来忘了加 @Transactional 注解。Spring 的事务是基于 AOP 的,必须加在 public 方法上,且不能 self-invocation(自己调自己)。


性能 & 架构小贴士(来自线上事故的教训)

我们去年双11期间,日志模块突然慢成狗。排查发现是 MyBatis 的 #{} ${} 混用了。

  • #{} 是预编译,防 SQL 注入,推荐用
  • ${} 是字符串替换,慎用!除非你知道自己在干啥(比如动态表名)

另外,别在循环里调 Mapper 方法!我见过实习生这么写:

for (Long userId : userIds) {
    User user = userMapper.findById(userId); // 每次都查 DB!
}

正确做法是用 IN 查询:

List<User> users = userMapper.findByIds(userIds);

对应的 XML:

<select id="findByIds" resultType="User">
    SELECT * FROM user WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

教程之外:怎么系统学习?

说实话,光看网上零散教程容易懵。我后来买了本《MyBatis 从入门到精通》(人民邮电出版社那本),配合官方文档,再跑几个 GitHub 开源项目,才真正搞明白。

如果你是学生党或刚入行,建议走这个路线:

  1. 先跑通一个完整 Demo(增删改查)
  2. 学会看 MyBatis 日志(加 logging.level.com.example.mapper=debug
  3. 尝试集成 PageHelper 做分页
  4. 了解一级缓存、二级缓存机制(虽然生产环境一般关掉)
  5. 看源码:SqlSession、Executor、StatementHandler 这几个核心类

最后:前端仔的感悟

作为一个 Vim 党,我习惯在终端里敲命令、看日志。MyBatis 的日志输出特别清晰,每次 SQL 执行、参数绑定都打出来,debug 起来比某些“智能”IDE 友好多了。

成都的生活节奏舒服,加班不多(除了产品临时改需求),这也让我有时间慢慢啃后端。现在我已经能独立接简单后端任务了,虽然离“架构师”还差十万八千里,但至少不会被产品经理一句话吓尿。

MyBatis 不是什么高深技术,但它代表了一种可控的灵活性——你既不用手写 JDBC 处理 ResultSet,也不用被 ORM 框架的魔法束缚。对新人来说,是个很好的中间态。

希望这篇综合教程能帮到和我一样的“转型选手”。如果觉得有用,欢迎留言交流(或者吐槽你们的产品经理)!

附:本文所有代码已上传 GitHub,搜 “mybatis-beginner-chengdu” 即可(假的,别真搜,我还没传 😅)

评论 0

最热最新
暂无评论
JSON搬运员Lv.1
0
影响力
0
文章
0
粉丝