MyBatis
详情
1. 是什么?
MyBatis 是一个开源的 持久层框架(Persistence Framework),用于简化 Java 应用与关系型数据库之间的交互。
MyBatis 只实现了 ORM 中的“M”(Mapping)部分,而没有实现“O-R”之间的自动同步与抽象。
所以严格来说它不是 ORM(对象关系映射)框架(不像 Hibernate 那样全自动映射),而是一个 SQL 映射框架(SQL Mapping Framework)。
它的核心思想是:开发者手写 SQL,MyBatis 负责将 SQL 的输入参数和输出结果自动映射到 Java 对象。
官方定义:MyBatis 是一个支持自定义 SQL、存储过程以及高级映射的优秀持久层框架。它避免了几乎所有的 JDBC 样板代码,同时保留了对 SQL 的完全控制。
关键特点:
- SQL 与 Java 代码分离(通常写在 XML 文件或注解中)
- 自动将 ResultSet 映射为 Java 对象(POJO)
- 支持动态SQL(如
<if>,<foreach>等标签) - 轻量、灵活、性能高
2. 没有之前?
在 MyBatis 出现之前(其前身是 Apache iBATIS,2002 年发布,2010 年更名为 MyBatis),Java 开发者主要依赖 原生 JDBC 访问数据库,存在以下问题:
| 问题 | 说明 |
|---|---|
| 大量样板代码 | 每次都要写 Connection、PreparedStatement、ResultSet 的获取、关闭、异常处理等。 |
| 结果集手动映射 | 需要逐字段从 ResultSet 中取值并赋给 Java 对象,重复且易错。 |
| SQL 与业务逻辑耦合 | SQL 字符串硬编码在 Java 类中,难以维护、测试和复用。 |
| 动态 SQL 困难 | 条件查询需拼接字符串,容易出错且不安全(如 SQL 注入风险)。 |
| 资源管理复杂 | 必须确保连接、语句、结果集正确关闭,否则可能内存泄漏。 |
3. 为什么使用?
使用 MyBatis 的核心价值在于 在保留 SQL 控制力的同时,消除 JDBC 的样板代码。
- SQL 完全可控:开发者手写 SQL,可优化、调试、复用,适合复杂查询。
- 减少样板代码:自动处理连接、参数设置、结果映射、资源关闭。
- 灵活的结果映射:支持一对一、一对多、嵌套结果、列别名自动匹配属性等。
- 强大的动态 SQL:通过 XML 标签(
<if>,<choose>,<foreach>)构建条件查询,安全且清晰。 - 轻量级 & 高性能:无运行时代理、无复杂缓存机制(除非显式配置),启动快、开销小。
- 易于集成:可与 Spring、Spring Boot 无缝整合。
- 学习成本低:相比 Hibernate,概念更少,上手更快。
适用场景:需要精细控制 SQL 的系统(如报表、大数据查询)、遗留系统改造、对性能敏感的应用。
4. 核心组件 / 概念
| 组件/概念 | 说明 |
|---|---|
| SqlSessionFactory | 重量级、线程安全的工厂类,用于创建 SqlSession。通常应用启动时初始化一次。 |
| SqlSession | 轻量级、非线程安全的会话对象,提供执行 SQL、获取 Mapper、管理事务的方法。 |
| Mapper 接口 | 纯 Java 接口,方法对应 SQL 操作。MyBatis 通过动态代理实现接口,无需编写实现类。 |
| Mapper XML 文件 | 定义 SQL 语句和结果映射规则(如 <select>, <resultMap>)。文件名需与 Mapper 接口对应。 |
| SqlSessionTemplate(Spring 中) | Spring 封装的线程安全 SqlSession,用于替代原始 SqlSession。 |
| TypeHandler | 类型处理器,用于 Java 类型与 JDBC 类型之间的转换(如枚举、JSON 字段)。 |
| ResultMap | 最强大的元素之一,用于定义复杂的结果映射(如关联对象、集合、列名与属性名不一致等)。 |
| 动态 SQL 标签 | 如 <if>, <where>, <foreach>, <set> 等,用于构建条件性 SQL。 |
5. 使用步骤
步骤 1:添加依赖(Maven)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 若使用 Spring Boot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>步骤 2:配置 MyBatis(mybatis-config.xml 或 Java Config)
<!-- mybatis-config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://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/mydb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 注册 Mapper XML 文件 -->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>步骤 3:定义实体类(POJO)
public class User {
private Long id;
private String name;
private String email;
// getter/setter/toString 略
}步骤 4:定义 Mapper 接口
public interface UserMapper {
User findById(Long id);
List<User> findByName(String name);
void insert(User user);
void update(User user);
void delete(Long id);
}步骤 5:编写 Mapper XML 文件(UserMapper.xml)
<!-- resources/mappers/UserMapper.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">
<select id="findById" resultType="com.example.model.User">
SELECT id, name, email FROM users WHERE id = #{id}
</select>
<select id="findByName" resultType="User">
SELECT * FROM users
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<update id="update">
UPDATE users
<set>
<if test="name != null">name = #{name},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>
<delete id="delete">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>步骤 6:使用 MyBatis(主程序)
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) throws Exception {
// 1. 加载配置
var inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 3. 获取 Mapper
UserMapper mapper = session.getMapper(UserMapper.class);
// 4. 执行操作
User user = new User();
user.setName("Bob");
user.setEmail("bob@example.com");
mapper.insert(user);
session.commit(); // 提交事务
User saved = mapper.findById(user.getId());
System.out.println(saved.getName()); // 输出 Bob
}
}
}在 Spring Boot 中,只需加
@MapperScan和application.yml配置,即可自动注入 Mapper。
6. 局限性(Limitations)
尽管 MyBatis 灵活高效,但也存在一些不足:
| 局限 | 说明 |
|---|---|
| 需要手写 SQL | 对简单 CRUD 场景显得“重”,不如 JPA/Hibernate 自动生成方便。 |
| 数据库移植性差 | SQL 写死在 XML/注解中,切换数据库需重写 SQL(如 MySQL 分页 vs Oracle ROWNUM)。 |
| 无对象关系自动管理 | 不支持懒加载、脏检查、级联保存等 ORM 特性,关联对象需手动处理。 |
| XML 维护成本 | 大型项目中 Mapper XML 文件可能非常多,管理复杂。 |
| 类型安全弱 | XML 中的 SQL 和参数名是字符串,编译期无法检查错误(但可通过 MyBatis Generator 缓解)。 |
| 缓存机制较弱 | 一级缓存(SqlSession 级)默认开启,二级缓存需手动配置且使用谨慎。 |
| 不适合快速原型开发 | 相比 Spring Data JPA,需要更多配置和文件。 |
关联网络
演化日志
- v0.1 (2024-02-26):初始版本
- v0.2 (2026-02-02):补充关联网络、演化日志
待办事项
- 懒加载、脏检查、级联保存等 ORM 特性
- Spring Boot
- 动态代理
- MyBatis-缓存算法
- 支持多种数据源,如POOLED(数据源)UNPOOLED(数据源)JNDI。同时,还可以整合其他的数据库连接池如HikariCP(高卡利)、Druid、C3P0等 ;