MyBatis 到底能帮你省下多少 JDBC 的重复代码?

502守望者
2026-05-22 20:01
阅读 295

大家好,我是小李,一个从培训班“毕业”、如今在一线做后端开发的普通程序员。还记得我当初学 Java Web 开发时,被 JDBC 折磨得死去活来——写个查询要处理 Connection、Statement、ResultSet,还要手动关资源,一不小心就内存泄漏。更别提 SQL 和 Java 代码混在一起,改个字段恨不得重写半页代码。

后来接触到 MyBatis,我才真正体会到什么叫“解放生产力”。今天,我就以一个过来人的身份,带完全零基础的朋友,手把手入门这个超实用的 Java 持久层框架。不讲虚的,只用最简单的语言 + 最贴近实战的例子,让你今天就能跑通第一个 MyBatis 程序。

更重要的是,文章末尾我还加了几个高频面试题挑战,帮你提前感受真实求职场景。准备好了吗?我们开始!


为什么你需要 MyBatis?

简单说:MyBatis 是一个帮助 Java 程序和数据库“友好对话”的工具

想象一下:你的 Java 程序想查用户信息,但数据库只会听 SQL。传统方式(JDBC)要求你:

  1. 手动加载驱动
  2. 手动获取连接
  3. 手动拼接 SQL 字符串(容易出错!)
  4. 手动把 ResultSet 转成 Java 对象
  5. 手动关闭所有资源

而 MyBatis 做了这些事:

✅ 自动管理数据库连接
✅ 把 SQL 写在 XML 或注解里(清晰分离)
✅ 自动把查询结果映射成 Java 对象(不用再写一堆 set/get)
✅ 支持动态 SQL(比如根据条件拼 WHERE)

一句话总结:MyBatis 让你专注业务逻辑,而不是重复造轮子


第一步:搭建开发环境(别跳过!)

💡 提示:以下步骤我当初踩过坑,务必按顺序操作!

所需工具清单

工具 版本建议 说明
JDK 8 或 11 Java 运行环境
Maven 3.6+ 项目依赖管理(推荐)
IDE IntelliJ IDEA / Eclipse 推荐 IDEA,对 MyBatis 支持更好
数据库 MySQL 5.7+ 本文以 MySQL 为例

创建 Maven 项目

打开终端或 IDE,执行:

mvn archetype:generate -DgroupId=com.example -DartifactId=mybatis-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

进入项目目录,编辑 pom.xml,添加核心依赖:

<dependencies>
    <!-- MyBatis 核心 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>

    <!-- 单元测试(方便验证) -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

⚠️ 注意:如果你用 MySQL 8.0+,记得在连接字符串里加 ?serverTimezone=UTC,否则会报时区错误!


核心概念:别被术语吓到

MyBatis 就像一个“翻译官”,它需要知道三件事:

  1. 怎么连数据库? → 配置文件 (mybatis-config.xml)
  2. SQL 写在哪? → 映射文件 (UserMapper.xml) 或 注解
  3. 查出来的数据变成啥对象? → Java Bean(比如 User.java

下面逐个拆解。

1. 配置文件:mybatis-config.xml

放在 src/main/resources 目录下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="your_password"/>
      </dataSource>
    </environment>
  </environments>

  <!-- 告诉 MyBatis 去哪找 SQL 映射文件 -->
  <mappers>
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

✅ 小贴士:POOLED 表示使用连接池,比每次新建连接高效得多。

2. Java Bean:User.java

package com.example;

public class User {
    private int id;
    private String name;
    private String email;

    // 必须有无参构造器(MyBatis 反射需要)
    public User() {}

    // Getter 和 Setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
    }
}

3. 映射文件:UserMapper.xml

同样放在 resources 目录:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.UserMapper">
  <!-- id 对应接口方法名,resultType 是返回类型 -->
  <select id="findById" resultType="com.example.User">
    SELECT id, name, email FROM users WHERE id = #{id}
  </select>
</mapper>

🔍 注意:#{id} 是安全的占位符,会自动防 SQL 注入!


实战:5 分钟写出第一个查询

我们来实现一个功能:根据 ID 查询用户。

步骤 1:创建数据库表

CREATE DATABASE testdb;
USE testdb;

CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50),
  email VARCHAR(100)
);

INSERT INTO users (name, email) VALUES 
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');

步骤 2:编写测试代码

src/test/java/com/example/MyBatisTest.java 中:

package com.example;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;

public class MyBatisTest {

    @Test
    public void testFindById() throws Exception {
        // 1. 读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        
        // 2. 创建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        
        // 3. 获取 SqlSession(相当于 JDBC 的 Connection)
        try (SqlSession session = sqlSessionFactory.openSession()) {
            // 4. 执行 SQL:namespace.id
            User user = session.selectOne("com.example.UserMapper.findById", 1);
            System.out.println(user); // 输出:User{id=1, name='张三', email='zhangsan@example.com'}
        }
    }
}

运行这个测试,如果看到控制台打印出用户信息,恭喜你!MyBatis 已经跑起来了!


更优雅的方式:使用 Mapper 接口

上面的方式虽然能用,但字符串 "com.example.UserMapper.findById" 容易写错。MyBatis 推荐用 接口 + 注解/映射文件 的方式。

1. 创建接口

package com.example;

public interface UserMapper {
    User findById(int id);
}

2. 修改 UserMapper.xml

只需把 namespace 改成接口全路径:

<mapper namespace="com.example.UserMapper">
  <select id="findById" resultType="com.example.User">
    SELECT id, name, email FROM users WHERE id = #{id}
  </select>
</mapper>

3. 修改测试代码

@Test
public void testWithMapper() throws Exception {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    try (SqlSession session = sqlSessionFactory.openSession()) {
        // 获取 Mapper 接口的代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.findById(1);
        System.out.println(user);
    }
}

现在,IDE 能自动提示方法名,再也不怕拼错!这就是 面向接口编程 的好处。


动态 SQL:条件查询也不怕

实际开发中,经常要根据条件查询。比如:“如果传了名字就按名字查,否则查全部”。

MyBatis 提供 <if><where> 等标签:

<select id="findByCondition" resultType="com.example.User">
  SELECT id, name, email FROM users
  <where>
    <if test="name != null and name != ''">
      AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="email != null and email != ''">
      AND email = #{email}
    </if>
  </where>
</select>

对应的接口方法:

List<User> findByCondition(@Param("name") String name, @Param("email") String email);

调用示例:

List<User> users = mapper.findByCondition("张", null); // 查名字含“张”的用户

@Param 注解用于给参数命名,XML 中用 #{name} 引用。


新手常见问题 & 避坑指南

❌ 问题1:ClassNotFoundException: Cannot find driver

原因:没加 MySQL 驱动依赖,或版本不对。
解决:检查 pom.xml 是否包含 mysql-connector-java,且 scope 不是 provided

❌ 问题2:Property 'dataSource' is required

原因mybatis-config.xml<dataSource> 配置缺失或拼写错误。
解决:仔细核对 driver、url、username、password 四项。

❌ 问题3:Invalid bound statement (not found)

原因:Mapper 接口方法名和 XML 中的 id 不一致,或 namespace 写错。
解决:确保三者一致:

  • 接口方法名 = XML 中 <select id="xxx">
  • namespace = 接口全限定名

❌ 问题4:中文乱码

解决:在 JDBC URL 后加上 &useUnicode=true&characterEncoding=utf8

jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8

面试题挑战:看看你能答对几道?

💡 这些都是我面试时被问过的真实题目!

题目1:MyBatis 中 #{} 和 ${} 有什么区别?

  • #{}预编译处理,会转成 ? 占位符,自动防 SQL 注入。
  • ${}字符串替换,直接拼接到 SQL 中,有注入风险,仅用于动态表名、列名等场景。

✅ 正确用法:WHERE id = #{id}
❌ 危险用法:WHERE id = ${id}(若 id 是用户输入,可能被注入)


题目2:MyBatis 如何实现分页?

两种方式:

  1. 物理分页:在 SQL 中加 LIMIT offset, size(推荐)
    <select id="findAll" resultType="User">
      SELECT * FROM users LIMIT #{offset}, #{size}
    </select>
    
  2. 逻辑分页:先查全部,再在 Java 中截取(数据量大时不推荐)

📌 高级方案:集成 PageHelper 插件,一行代码自动分页。


题目3:什么是 Embedding?MyBatis 支持吗?

注意!这里有个陷阱——Embedding(嵌入)通常指 AI 领域的向量表示,和 MyBatis 无关

但在数据库上下文中,有人误以为“嵌套查询”叫 Embedding。实际上,MyBatis 支持:

  • 关联查询(一对一、一对多)通过 <association><collection>
  • 嵌套结果映射

例如:查询用户及其订单:

<resultMap id="UserWithOrders" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
  <collection property="orders" ofType="Order">
    <id property="id" column="order_id"/>
    <result property="amount" column="amount"/>
  </collection>
</resultMap>

所以,面试时如果听到“Embedding”,先确认上下文!大概率是面试官口误,实际想问“嵌套查询”或“关联映射”。


下一步学习建议

  1. 掌握 CRUD 全套操作:增删改查都要动手写一遍
  2. 学习缓存机制:MyBatis 有一级缓存(SqlSession 级)、二级缓存(Mapper 级)
  3. 尝试注解方式@Select, @Insert 等,适合简单 SQL
  4. 集成 Spring Boot:实际项目几乎都用 mybatis-spring-boot-starter
  5. 阅读官方文档https://mybatis.org/mybatis-3/zh/

🌟 最后提醒:不要死记硬背!每个概念都自己敲代码验证。我当初就是靠“边学边删边重写”才真正掌握的。


结语

MyBatis 并不难,难的是迈出第一步。希望这篇教程能帮你绕过我当年踩过的坑。记住:所有复杂的框架,底层都是为了解决简单的问题

现在,打开你的 IDE,创建那个 mybatis-demo 项目吧!跑通第一个查询的那一刻,你会感谢今天的自己。

加油,未来的 Java 工程师!

评论 0

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