Skip to content

MyBatis

MyBatis 是企业 Java 开发里非常常见的数据访问框架。

很多人对它的第一印象是“就是写 XML 的”,这个理解太窄了。

更贴近的说法是:MyBatis 在保留 SQL 控制力的前提下,把原始 JDBC 里重复、繁琐、容易出错的模板代码收起来。

这篇文章重点讲:

  1. MyBatis 到底是什么
  2. 它解决什么问题
  3. 它和 JDBC、JPA 的区别是什么
  4. Mapper、SqlSession、动态 SQL 这些词分别是什么意思
  5. 企业里为什么大量项目会选它

1. MyBatis 到底是什么

MyBatis 可以看成一个以 SQL 为中心的数据访问框架。

它不会像 ORM 那样尽量隐藏 SQL,而是让开发者继续掌控 SQL,同时帮你处理参数绑定、结果映射和执行流程。

所以它的核心风格可以直接记成:SQL 还是你写,但 JDBC 那些重复劳动它替你收掉。


2. 它主要解决什么问题

原始 JDBC 常见的问题包括:

  1. 连接、语句、结果集代码很多
  2. 参数绑定和结果集映射重复
  3. SQL 和 Java 代码容易缠在一起
  4. 动态查询写起来很别扭

MyBatis 要解决的核心问题就是:保留 SQL 控制力,同时降低 JDBC 模板代码成本。


3. 它和 JDBC、JPA 分别是什么关系

3.1 和 JDBC 的关系

JDBC 更底层,MyBatis 更高一层。

  1. JDBC 是数据库访问标准接口
  2. MyBatis 是建立在 JDBC 之上的框架

3.2 和 JPA 的关系

MyBatisJPA 代表的是两种不同思路。

  1. MyBatis 更偏 SQL 驱动
  2. JPA 更偏对象模型驱动

如果项目 SQL 很复杂、分页和报表很多、查询控制要求高,团队往往更倾向 MyBatis


4. Mapper 到底是什么

Mapper 可以理解成把 Java 方法和 SQL 执行关联起来的一层接口抽象。

它解决的是:不要每次都手动写一堆 JDBC 调用,而是通过接口方法表达数据访问动作。

例如:

java
/**
 * 最基础的用户 Mapper 接口示例。
 */
@Mapper
public interface UserMapper {

    /**
     * 按用户 ID 查询用户。
     *
     * @param id 用户主键 ID
     * @return 查询到的用户对象
     */
    User selectById(Long id);

    /**
     * 插入一条用户记录。
     *
     * @param user 待写入数据库的用户对象
     * @return 受影响行数
     */
    int insert(User user);
}

这个接口本身不是“会自动查数据库”,而是 MyBatis 会把它和对应 SQL 映射关系关联起来。


5. XML 和注解两种写法怎么理解

MyBatis 最常见的两种写法通常是:

  1. XML 映射
  2. 注解映射

5.1 XML 映射

它更适合 SQL 比较复杂、动态条件多、需要较强可维护性时。

例如:

xml
<select id="selectById" resultType="com.example.User">
    select id, name, age
    from user
    where id = #{id}
</select>

5.2 注解映射

它更适合 SQL 简短直接、接口数量不多时。

例如:

java
/**
 * 使用注解方式直接声明 SQL 的用户 Mapper。
 */
@Mapper
public interface UserMapper {

    /**
     * 按用户 ID 查询用户信息。
     *
     * @param id 用户主键 ID
     * @return 查询到的用户对象
     */
    @Select("select id, name, age from user where id = #{id}")
    User selectById(Long id);
}
  1. SQL 简单时,注解更轻
  2. SQL 复杂时,XML 更稳

6. #{}${} 有什么区别

这是 MyBatis 里非常关键的一组边界。

6.1 #{}

它更像预编译参数占位。

更安全,也更常用。

6.2 ${}

它更像字符串直接拼接。

灵活,但风险更大。

要注意的是:大多数普通参数绑定都应该优先用 #{}

例如排序字段、表名这类动态拼接场景,才可能涉及 ${},而这时必须特别注意注入风险。


7. 结果映射到底在做什么

数据库查询出来的是结果集,业务里更常希望拿到 Java 对象。

结果映射 解决的就是:怎么把查询结果映射成 Java 对象。

最简单的情况可以直接靠字段同名映射; 更复杂时则会用到 resultMap

例如:

xml
<resultMap id="userResultMap" type="com.example.User">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
</resultMap>

<select id="selectById" resultMap="userResultMap">
    select id, user_name from user where id = #{id}
</select>

8. 动态 SQL 到底解决什么问题

企业项目里查询条件经常不是固定的。

例如:

  1. 用户名可选
  2. 状态可选
  3. 时间区间可选

如果全部手写字符串拼接,很容易乱。

MyBatis 的动态 SQL 解决的是:根据条件动态生成 SQL,同时保持结构可维护。

例如:

xml
<select id="queryUsers" resultType="com.example.User">
    select id, name, age
    from user
    <where>
        <if test="name != null and name != ''">
            and name = #{name}
        </if>
        <if test="age != null">
            and age = #{age}
        </if>
    </where>
</select>

9. SqlSession 是什么

SqlSession 可以理解成 MyBatis 执行 SQL、管理映射操作的一次会话入口

它在底层很重要,但在 Spring Boot 整合场景下,很多时候不会直接手写操作它。

更常见的情况是:你直接注入 Mapper,Spring 和 MyBatis 帮你把底层会话协同好了。


10. MyBatis 和 Spring Boot 怎么整合

在企业项目里,比较常见的接入方式就是:

  1. 引入 mybatis-spring-boot-starter
  2. 配置数据源
  3. 扫描 Mapper
  4. 编写接口和 XML / 注解 SQL

例如启动类或配置类里常见:

java
/**
 * MyBatis 与 Spring Boot 整合时的启动入口。
 */
@SpringBootApplication
@MapperScan("com.example.mapper")
public class DemoApplication {

    /**
     * 启动当前应用,并让 Spring 扫描 Mapper 接口。
     *
     * @param args 命令行启动参数
     */
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

对应配置:

yaml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo
    username: root
    password: 123456

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

11. MyBatis 常用注解和组件

注解 / 组件作用
@Mapper标记 Mapper 接口
@MapperScan批量扫描 Mapper
@Select / @Insert / @Update / @Delete注解方式定义 SQL
SqlSessionMyBatis 会话入口
resultMap自定义结果映射

例如:

java
/**
 * 订单表对应的数据访问接口。
 */
@Mapper
public interface OrderMapper {

    /**
     * 按订单 ID 更新订单状态。
     *
     * @param id 订单主键 ID
     * @param status 目标状态值
     * @return 受影响行数
     */
    @Update("update orders set status = #{status} where id = #{id}")
    int updateStatus(@Param("id") Long id, @Param("status") String status);
}

12. 企业里为什么很多项目会选 MyBatis

比较常见的原因包括:

  1. SQL 控制力强
  2. 复杂查询表达直接
  3. 对 DBA、SQL 调优协作更友好
  4. 对报表、列表、统计场景很适合

所以它在很多中后台系统里非常常见。


13. 工程上最容易踩的几个坑

13.1 误区一:用了 MyBatis 就不需要理解数据库

不是。

索引、事务、锁、执行计划这些问题仍然要回到数据库本身。

13.2 误区二:${}#{} 只是写法不同

不是。

它们在安全性和参数处理方式上差异非常大。

13.3 误区三:Mapper 方法写出来就一定高性能

性能最终还是取决于 SQL、索引、分页、批量策略和数据库设计。


14. 一句话总结

MyBatis 的核心价值,是在保留 SQL 控制力的前提下,把原始 JDBC 的模板代码、参数绑定和结果映射收口成更清晰的数据访问模型;而企业里真正要把它用好,关键仍然是 SQL 设计、数据库理解和与 Spring 事务链路的协同。

基于 VitePress 构建的个人技术笔记。