别再被ORM吓退!MyBatis零基础入门实战指南

徐雨萱
2026-05-03 02:37
阅读 1705

你好,我是你们的技术培训负责人老张。带过上百名应届生,深知刚接触后端开发时面对“持久层”、“ORM”这些词的茫然感。我当初学的时候,光是配个数据库连接就折腾了一整天——不是驱动版本不对,就是XML路径写错。更别提面试官冷不丁问一句:“MyBatis和Hibernate有什么区别?”当场懵圈。

今天这篇教程,就是专为完全零基础的同学准备的。哪怕你连JDBC都没碰过,只要会写简单的Java类,就能跟着做下去。我们会用最直白的语言、最真实的代码、最常见的坑,带你亲手跑通第一个MyBatis程序。

顺便说一句:虽然标题里有“Python”,但本文主角是Java生态的MyBatis。之所以提到Python,是因为很多转行同学是从Python过来的(比如我带过的不少数据方向转后端的同学),会在对比中产生困惑。文末我会专门聊聊这点。


为什么选MyBatis?它到底是什么?

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

想象一下,你想从MySQL里查一个用户信息。不用框架的话,你得:

  1. 加载数据库驱动
  2. 建立连接(Connection)
  3. 写SQL字符串
  4. 创建Statement或PreparedStatement
  5. 执行查询
  6. 遍历ResultSet把数据塞进Java对象
  7. 关闭各种资源……

光是第6步,字段名对不上、类型转换出错,就够你调试半天。而MyBatis把这些脏活累活全包了——你只需专注两件事:

  • 写SQL(对,你没看错,MyBatis让你直接写SQL!)
  • 定义Java对象和数据库表的映射关系

这正是它比Hibernate更受国内企业欢迎的原因:灵活、可控、性能透明。很多大厂面试题都会考:“为什么项目选用MyBatis而不是JPA?” 答案往往就是这两个字:掌控力


环境准备:5分钟搭好开发环境

⚠️ 注意:本教程基于 Java 8+、Maven、MySQL 5.7+。如果你用的是IDEA,会更顺畅。

第一步:创建Maven项目

打开IDEA → New Project → 选择Maven → 勾选“Create from archetype” → 选maven-archetype-quickstart

输入:

  • GroupId: com.example
  • ArtifactId: mybatis-demo

点Finish。等待项目初始化完成。

第二步:添加依赖(pom.xml)

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>

保存后,IDEA会自动下载依赖。如果卡住,检查网络或手动刷新Maven。

第三步:准备数据库

登录MySQL,执行:

CREATE DATABASE mybatis_demo;
USE mybatis_demo;

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

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

记下你的数据库用户名和密码(假设是root/123456)。


核心概念:三件套搞定MyBatis

MyBatis的核心配置就三个文件,我称之为“铁三角”:

文件 作用 位置
mybatis-config.xml 全局配置:数据库连接、插件等 src/main/resources
UserMapper.java 接口:定义操作数据库的方法 src/main/java/com/example/mapper
UserMapper.xml SQL映射:写具体SQL语句 src/main/resources

下面一一详解。

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

src/main/resources下新建mybatis-config.xml

<?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/mybatis_demo?useSSL=false&amp;serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>

  <!-- 注册Mapper XML文件 -->
  <mappers>
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

💡 踩坑提示:

  • &amp; 是XML里的转义写法,实际URL中是 &
  • 如果报“时区错误”,确保加了 serverTimezone=UTC
  • 驱动类名在MySQL 8+是 com.mysql.cj.jdbc.Driver,不是旧的 com.mysql.jdbc.Driver

2. 定义实体类:User.java

src/main/java/com/example/model 下创建:

package com.example.model;

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

    // 必须要有无参构造器!MyBatis通过反射创建对象
    public User() {}

    // getter/setter省略(IDEA可自动生成)
    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 + "'}";
    }
}

⚠️ 新手常犯错误:忘记写无参构造器!MyBatis底层用反射new User(),没有无参构造会报错。

3. Mapper接口与XML:解耦SQL与Java

先创建接口 UserMapper.java(在 com.example.mapper 包下):

package com.example.mapper;

import com.example.model.User;
import java.util.List;

public interface UserMapper {
    List<User> findAll();
    User findById(int id);
    void insert(User user);
    void update(User user);
    void delete(int id);
}

再创建对应的 UserMapper.xml(放在 src/main/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.mapper.UserMapper">

  <!-- 结果映射:告诉MyBatis如何把数据库列转成Java属性 -->
  <resultMap id="UserResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="email" column="email"/>
  </resultMap>

  <!-- 查询所有用户 -->
  <select id="findAll" resultMap="UserResultMap">
    SELECT * FROM user
  </select>

  <!-- 根据ID查询 -->
  <select id="findById" parameterType="int" resultMap="UserResultMap">
    SELECT * FROM user WHERE id = #{id}
  </select>

  <!-- 插入用户 -->
  <insert id="insert" parameterType="com.example.model.User">
    INSERT INTO user (name, email) VALUES (#{name}, #{email})
  </insert>

  <!-- 更新用户 -->
  <update id="update" parameterType="com.example.model.User">
    UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
  </update>

  <!-- 删除用户 -->
  <delete id="delete" parameterType="int">
    DELETE FROM user WHERE id = #{id}
  </delete>

</mapper>

🔑 关键点解释:

  • namespace 必须和接口全类名一致!
  • #{} 是MyBatis的占位符,会自动防SQL注入(类似PreparedStatement)。
  • resultMap 用于复杂映射(比如列名和属性名不一致时)。这里简单场景其实可以用 resultType="User" 直接映射,但养成用resultMap的习惯更安全。

实战:跑通第一个MyBatis程序

现在,我们写一个测试类来验证一切是否正常。

src/test/java/com/example 下创建 MyBatisTest.java

package com.example;

import com.example.mapper.UserMapper;
import com.example.model.User;
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.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {

    @Test
    public void testFindAll() throws IOException {
        // 1. 读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        
        // 2. 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        
        // 3. 获取SqlSession(代表一次数据库会话)
        try (SqlSession session = sqlSessionFactory.openSession()) {
            // 4. 获取Mapper接口的代理实现
            UserMapper mapper = session.getMapper(UserMapper.class);
            
            // 5. 调用方法
            List<User> users = mapper.findAll();
            for (User user : users) {
                System.out.println(user);
            }
        }
    }

    @Test
    public void testInsert() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            
            User newUser = new User();
            newUser.setName("王五");
            newUser.setEmail("wangwu@example.com");
            
            mapper.insert(newUser);
            // ⚠️ 重要:MyBatis默认不会自动提交事务!
            session.commit();
            
            System.out.println("插入成功,新ID: " + newUser.getId()); // 注意:此时ID还是0!
        }
    }
}

运行 testFindAll(),你应该看到控制台输出两个用户信息。

💡 踩坑重点:

  • 必须调用 session.commit()!否则插入/更新不会生效。这是新手最高频的“为什么数据没变?”问题。
  • 插入后想拿到自增ID?需要在 <insert> 标签里加属性:
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
    
    这样 newUser.getId() 才能拿到数据库生成的ID。

新手高频问题解答(附真实面试题)

Q1:MyBatis和Hibernate有什么区别?(经典面试题!)

对比项 MyBatis Hibernate
SQL控制 开发者写SQL,完全掌控 自动生成SQL,黑盒感强
学习曲线 简单,会SQL就能上手 复杂,需理解Session、缓存等概念
性能优化 直接优化SQL即可 需要懂HQL、缓存策略等
适用场景 高并发、复杂查询、遗留系统 快速开发、简单CRUD、新项目

📌 面试回答技巧:
“我们项目选MyBatis,因为DBA要求SQL必须经过审核,且部分查询涉及多表复杂关联,手写SQL更高效。”

Q2:为什么我的XML文件改了没生效?

90%是因为IDE没把resources目录标记为资源目录
解决:右键 src/main/resources → Mark Directory as → Resources Root。

Q3:Python开发者常有的困惑

很多从Python转来的同学会问:“Django ORM那么方便,MyBatis还要写XML,是不是落后了?”

答:不是落后,是权衡

  • Python的GIL限制了多线程性能,所以Django靠ORM简化开发没问题。
  • Java常用于高并发场景,MyBatis让开发者能精确控制SQL,避免ORM生成低效查询拖垮数据库。

记住:没有银弹,只有合适场景

Q4:#{xxx} 和 ${xxx} 有什么区别?

  • #{xxx} → PreparedStatement占位符,自动防SQL注入
  • ${xxx} → 字符串拼接,有SQL注入风险!

除非动态表名/列名(如分表),否则永远用 #{}


下一步学习建议

恭喜你完成了MyBatis入门!但真正的工程化使用远不止这些。建议按顺序深入:

  1. 整合Spring Boot:告别手动管理SqlSession,用@MapperScan自动注入。
  2. 学习动态SQL<if>, <foreach> 等标签处理条件查询。
  3. 掌握缓存机制:一级缓存(SqlSession级)、二级缓存(Mapper级)。
  4. 分页插件:PageHelper是国产神器,几行代码实现物理分页。
  5. 逆向工程:用MyBatis Generator自动生成Mapper和实体类。

最后送你一句我常对应届生说的话:
“框架只是工具,SQL才是后端工程师的硬通货。”
把MyBatis当桥梁,真正去理解索引、事务、锁这些数据库核心知识,你才能在面试中脱颖而出。

现在,去跑通你的第一个MyBatis程序吧!遇到问题,欢迎回来翻这篇“避坑指南”。

评论 0

最热最新
暂无评论
徐雨萱Lv.1
0
影响力
0
文章
0
粉丝