目录

一、MyBatis是什么

1.1 一句话定义

1.2 它在哪个位置

1.3 核心特点

1.4 MyBatis vs 其他ORM框架

二、为什么需要MyBatis

2.1 先看看原生JDBC的问题

2.2 JDBC的5大痛点

2.3 MyBatis怎么解决这些问题

三、核心组件

3.1 SqlSessionFactory — 工厂

3.2 SqlSession — 会话

3.3 Mapper — 接口

3.4 三者的关系

四、XML映射与动态SQL

4.1 XML映射文件结构

4.2 基本CRUD

4.3 #{} 与 ${} 的区别

4.4 动态SQL

— 条件判断

— 智能WHERE

— 智能SET(用于UPDATE)

— 遍历集合

4.5 ResultMap — 结果映射

五、注解开发

5.1 什么是注解开发

5.2 常用注解

5.3 注解 vs XML 对比

总结

本篇要点回顾

面试高频考点

下篇预告

本文从"为什么需要MyBatis"出发,带你掌握这个优秀持久层框架的核心知识。


一、MyBatis是什么

1.1 一句话定义

MyBatis 是一款优秀的半自动 ORM 持久层框架。

拆解这个定义:

  • 持久层:负责将数据保存到数据库(持久化),Java 中就是与数据库交互的那一层代码
  • ORM:Object-Relational Mapping,对象关系映射,把数据库表映射为 Java 对象
  • 半自动:开发者需要手写 SQL,框架负责将结果映射到对象(区别于 Hibernate 的全自动)

1.2 它在哪个位置

在 Java Web 的三层架构中,MyBatis 处于持久层(Dao 层):

┌─────────────────┐
│   Controller    │  ← 接收请求,返回响应
├─────────────────┤
│    Service      │  ← 业务逻辑
├─────────────────┤
│   Mapper/Dao    │  ← 数据库操作(MyBatis 在这里)
├─────────────────┤
│    Database     │  ← MySQL、Oracle 等
└─────────────────┘

1.3 核心特点

  1. SQL 与代码分离:SQL 写在 XML 文件或注解中,Java 代码负责业务逻辑
  2. 灵活的动态 SQL:支持 <if><where><foreach> 等标签
  3. 自动结果映射:将数据库查询结果自动映射为 Java 对象
  4. 学习成本低:相比 Hibernate,更接近原生 SQL

1.4 MyBatis vs 其他ORM框架

框架类型SQL 控制学习成本适用场景
JDBC原生完全手动底层理解
MyBatis半自动手写 SQL复杂查询、性能优化
Hibernate全自动框架生成简单 CRUD、快速开发
Spring Data JPA全自动方法命名规则Repository 模式

二、为什么需要MyBatis

2.1 先看看原生JDBC的问题

在没有 MyBatis 之前,Java 操作数据库只能用 JDBC:

// 原生 JDBC 查询用户
public User findById(Long id) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    User user = null;
    
    try {
        // 1. 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2. 获取连接
        conn = DriverManager.getConnection(url, username, password);
        // 3. 编写 SQL
        String sql = "SELECT * FROM user WHERE id = ?";
        // 4. 创建 Statement
        ps = conn.prepareStatement(sql);
        ps.setLong(1, id);
        // 5. 执行查询
        rs = ps.executeQuery();
        // 6. 处理结果集
        if (rs.next()) {
            user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            user.setAge(rs.getInt("age"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 7. 关闭资源
        try { if (rs != null) rs.close(); } catch (Exception e) {}
        try { if (ps != null) ps.close(); } catch (Exception e) {}
        try { if (conn != null) conn.close(); } catch (Exception e) {}
    }
    return user;
}

2.2 JDBC的5大痛点

痛点具体问题
代码繁琐每次都要注册驱动、获取连接、关闭资源,重复代码多
SQL耦合SQL写在Java代码中,修改SQL需要重新编译
参数设置麻烦手动setXxx()设置参数,容易出错
结果映射繁琐手动getXxx()处理结果集,字段多了很痛苦
资源管理复杂需要手动关闭Connection、Statement、ResultSet

2.3 MyBatis怎么解决这些问题

JDBC痛点MyBatis解决方案
代码繁琐封装JDBC,自动管理连接和资源
SQL耦合SQL写在XML文件中,与Java代码分离
参数设置麻烦使用#{}自动设置参数
结果映射繁琐使用resultType自动映射到Java对象
资源管理复杂框架自动关闭资源

代码量对比

// JDBC方式:30+ 行代码
Connection conn = DriverManager.getConnection(url, username, password);
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
    user.setId(rs.getLong("id"));
    user.setName(rs.getString("name"));
}
// 还要关闭资源...

// MyBatis方式:1 行代码
User user = userMapper.findById(id);

三、核心组件

MyBatis 有三个核心组件,理解它们的关系是掌握 MyBatis 的关键:

┌──────────────────┐
│ SqlSessionFactory │  ← 工厂(创建 SqlSession)
└────────┬─────────┘
         │ 创建
         ▼
┌──────────────────┐
│    SqlSession    │  ← 会话(执行 SQL)
└────────┬─────────┘
         │ 获取
         ▼
┌──────────────────┐
│     Mapper       │  ← 接口(定义操作)
└──────────────────┘

3.1 SqlSessionFactory — 工厂

是什么:SqlSessionFactory 是创建 SqlSession 的工厂,整个应用中只有一个实例。

特点

  • 线程安全
  • 应用启动时创建,全局唯一
  • 负责创建 SqlSession 对象

怎么创建

// 方式1:基于XML配置(传统方式)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 方式2:Spring Boot中自动配置(推荐)
// 只需在application.yml中配置,Spring Boot自动创建

3.2 SqlSession — 会话

是什么:SqlSession 是 MyBatis 与数据库交互的核心接口,一次数据库会话。

特点

  • 线程不安全(每次使用后应该关闭)
  • 提供了执行 SQL 的方法
  • 管理事务(commit、rollback)

核心方法

public interface SqlSession {
    // 查询
    <T> T selectOne(String statement, Object parameter);
    <E> List<E> selectList(String statement, Object parameter);
    
    // 增删改
    int insert(String statement, Object parameter);
    int update(String statement, Object parameter);
    int delete(String statement, Object parameter);
    
    // 获取Mapper
    <T> T getMapper(Class<T> type);
    
    // 事务管理
    void commit();
    void rollback();
    void close();
}

3.3 Mapper — 接口

是什么:Mapper 是定义数据库操作的接口,MyBatis 通过动态代理生成实现类。

特点

  • 只需要定义接口,不需要写实现类
  • MyBatis 运行时通过 JDK 动态代理生成实现
  • 方法名对应 XML 中的 SQL id

定义 Mapper 接口

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

使用 Mapper

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;  // Spring自动注入代理对象
    
    public User getUser(Long id) {
        return userMapper.findById(id);
    }
}

3.4 三者的关系

应用启动
    ↓
创建 SqlSessionFactory(读取配置)
    ↓
打开 SqlSession(获取数据库连接)
    ↓
获取 Mapper(代理对象)
    ↓
调用 Mapper 方法(执行 SQL)
    ↓
关闭 SqlSession(释放资源)

四、XML映射与动态SQL

4.1 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">
    
    <!-- SQL语句 -->
    
</mapper>

关键点

  • namespace:对应 Mapper 接口的全限定名
  • 每个 SQL 语句对应一个标签:<select><insert><update><delete>

4.2 基本CRUD

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

<!-- 插入并返回自增主键 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (name, age, email)
    VALUES (#{name}, #{age}, #{email})
</insert>

<!-- 更新 -->
<update id="update">
    UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>

<!-- 删除 -->
<delete id="deleteById">
    DELETE FROM user WHERE id = #{id}
</delete>

4.3 #{} 与 ${} 的区别

这是面试高频考点

语法说明安全性
#{}预编译占位符✅ 安全,防止SQL注入
${}字符串拼接❌ 不安全,有SQL注入风险
<!-- ✅ 推荐:使用#{} -->
<select id="findById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>
<!-- 实际执行:SELECT * FROM user WHERE id = ? -->

<!-- ❌ 不推荐:使用${} -->
<select id="findById" resultType="User">
    SELECT * FROM user WHERE id = ${id}
</select>
<!-- 实际执行:SELECT * FROM user WHERE id = 1 -->

什么时候用${}

  • 动态表名:SELECT * FROM ${tableName}
  • 动态列名:ORDER BY ${columnName}

4.4 动态SQL

动态 SQL 是 MyBatis 的强大特性,根据不同条件动态生成 SQL。

<if> — 条件判断
<select id="findByNameAndAge" resultType="User">
    SELECT * FROM user
    WHERE 1 = 1
    <if test="name != null and name != ''">
        AND name = #{name}
    </if>
    <if test="age != null">
        AND age = #{age}
    </if>
</select>
<where> — 智能WHERE
<select id="findByNameAndAge" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

<where>的作用

  • 自动添加 WHERE 关键字
  • 自动去除多余的 ANDOR
  • 如果没有条件,不生成 WHERE
<set> — 智能SET(用于UPDATE)
<update id="update">
    UPDATE user
    <set>
        <if test="name != null">name = #{name},</if>
        <if test="age != null">age = #{age},</if>
        <if test="email != null">email = #{email},</if>
    </set>
    WHERE id = #{id}
</update>
<foreach> — 遍历集合
<!-- 根据ID列表查询 -->
<select id="findByIds" resultType="User">
    SELECT * FROM user
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量插入 -->
<insert id="batchInsert">
    INSERT INTO user (name, age) VALUES
    <foreach collection="users" item="user" separator=",">
        (#{user.name}, #{user.age})
    </foreach>
</insert>

4.5 ResultMap — 结果映射

当数据库字段名和Java属性名不一致时:

<resultMap id="userResultMap" type="User">
    <id property="userId" column="user_id"/>
    <result property="userName" column="user_name"/>
    <result property="createTime" column="create_time"/>
</resultMap>

<select id="findById" resultMap="userResultMap">
    SELECT * FROM user WHERE id = #{id}
</select>

五、注解开发

5.1 什么是注解开发

MyBatis 支持用注解直接在 Mapper 接口上定义 SQL,不需要 XML 文件。

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);
}

5.2 常用注解

@Mapper
public interface UserMapper {
    
    // 查询
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);
    
    // 插入
    @Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);
    
    // 更新
    @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
    int update(User user);
    
    // 删除
    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteById(Long id);
}

5.3 注解 vs XML 对比

特性注解方式XML方式
可读性简单SQL直观复杂SQL清晰
动态SQL有限支持完整支持
复杂映射有限支持完整支持
适用场景简单CRUD复杂查询、动态SQL

最佳实践:混合使用

@Mapper
public interface UserMapper {
    
    // 简单查询用注解
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);
    
    // 复杂查询用XML
    List<User> findByCondition(UserQuery query);
}

总结

本篇要点回顾

  1. MyBatis是什么:半自动ORM持久层框架
  2. 为什么需要它:解决JDBC的5大痛点
  3. 核心组件:SqlSessionFactory → SqlSession → Mapper
  4. XML映射:SQL与代码分离
  5. 动态SQL<if><where><foreach>
  6. 注解开发:简单SQL用注解,复杂SQL用XML

面试高频考点

  • MyBatis与JDBC的对比
  • #{}${}的区别
  • MyBatis的执行流程
  • 动态SQL的使用

下篇预告

下篇将介绍:

  • MyBatis的缓存机制(一级缓存、二级缓存)
  • MyBatis-Plus的增强功能

下篇链接MyBatis从入门到精通(下):缓存机制与MyBatis-Plus

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐