【MyBatis】从入门到精通(上)
·
目录
本文从"为什么需要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 核心特点
- SQL 与代码分离:SQL 写在 XML 文件或注解中,Java 代码负责业务逻辑
- 灵活的动态 SQL:支持
<if>、<where>、<foreach>等标签 - 自动结果映射:将数据库查询结果自动映射为 Java 对象
- 学习成本低:相比 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关键字 - 自动去除多余的
AND或OR - 如果没有条件,不生成
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);
}
总结
本篇要点回顾
- MyBatis是什么:半自动ORM持久层框架
- 为什么需要它:解决JDBC的5大痛点
- 核心组件:SqlSessionFactory → SqlSession → Mapper
- XML映射:SQL与代码分离
- 动态SQL:
<if>、<where>、<foreach> - 注解开发:简单SQL用注解,复杂SQL用XML
面试高频考点
- MyBatis与JDBC的对比
#{}与${}的区别- MyBatis的执行流程
- 动态SQL的使用
下篇预告
下篇将介绍:
- MyBatis的缓存机制(一级缓存、二级缓存)
- MyBatis-Plus的增强功能
更多推荐




所有评论(0)