基础概念
MyBatis 使用教程、配置指南与常见问题
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发,提升数据库操作的灵活性和性能。它通过 XML 或注解将 SQL 语句与 Java 代码解耦,支持动态 SQL、缓存和事务管理。以下内容基于官方文档和最佳实践,结合引用资料,逐步介绍 MyBatis 的使用方法、配置步骤和常见问题解决方案。回答中引用了相关段落,末尾添加了引用标识。
一、MyBatis 简介
MyBatis 起源于 Apache iBatis 项目,2010 年更名为 MyBatis 并迁移至 GitHub。它通过 SQL 映射文件或注解,将 Java 对象与数据库表关联,支持复杂查询和事务控制。核心优势包括:
- 简化 JDBC 冗余代码。
- 提供动态 SQL 能力,适应复杂查询需求。
- 支持缓存机制提升性能。
二、配置指南
配置 MyBatis 是使用框架的基础,主要包括创建配置文件和设置数据库连接。以下是详细步骤:
-
创建配置文件 (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"/> <!-- 使用 JDBC 事务管理 --> <dataSource type="POOLED"> <!-- 连接池优化性能 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> <!-- 加载映射文件 --> </mappers> </configuration>- 关键配置项:
environments: 定义数据库环境(如开发或生产)。dataSource: 使用POOLED类型提升连接复用效率。mappers: 指定 Mapper XML 文件路径。
- 此配置确保 MyBatis 正确初始化,并避免连接泄露问题。
- 关键配置项:
-
配置映射器 (Mapper)
映射器将 SQL 语句绑定到 Java 接口。创建 Mapper 接口和 XML 文件:- Mapper 接口示例:
package com.example.mapper; public interface UserMapper { List<User> findAll(); // 定义查询方法 } - Mapper XML 文件 (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="findAll" resultType="user"> <!-- resultType 指定返回对象类型 --> SELECT * FROM user </select> </mapper> - 注意:
namespace必须与 Mapper 接口全限定名一致,否则会报绑定错误。
- Mapper 接口示例:
三、使用教程
MyBatis 的核心是执行 SQL 操作。以下从基本查询到高级功能逐步说明:
-
基本查询
使用 Mapper XML 编写 SQL,并在 Java 代码中调用:- 示例代码:
// 加载配置并获取 SqlSession 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); List<User> users = mapper.findAll(); // 执行查询 System.out.println(users); } - 结果封装:
resultType="user"自动将查询结果映射到User对象,要求数据库列名与 Java 属性名匹配。
- 示例代码:
-
动态 SQL
处理条件查询时,使用 MyBatis 动态标签避免 SQL 拼接错误:- XML 示例:
<select id="findByCondition" resultType="user"> SELECT * FROM user <where> <if test="name != null">AND name = #{name}</if> <if test="age != null">AND age = #{age}</if> </where> </select> - 说明:
<where>标签自动处理AND前缀,#{name}使用参数占位符防止 SQL 注入。
- XML 示例:
-
高级查询与分页
结合插件(如 PageHelper)实现分页:- 单元测试示例(基于引用):
@SpringBootTest public class UserTest { @Autowired private UserMapper userMapper; @Test public void testQuery() { QueryWrapper wrapper = QueryWrapper.create() .select() .where(ACCOUNT.AGE.eq(18)); // 使用条件构造器 User user = userMapper.selectOneByQuery(wrapper); System.out.println(user); } } - 优化建议:分页查询时,添加
LIMIT子句减少内存占用。
- 单元测试示例(基于引用):
四、常见问题与优化技巧
MyBatis 使用中常见错误涉及配置、SQL 编写和性能。以下是解决方案:
-
SQL 注入风险
- 问题:直接拼接 SQL 字符串可能导致注入攻击。
- 解决:始终使用
#{}占位符(如#{param})代替${},因为#{}会预编译参数。例如:
避免使用<select id="findByName" resultType="user"> SELECT * FROM user WHERE name = #{name} <!-- 安全 --> </select>SELECT * FROM user WHERE name = ${name}(危险)。
-
性能优化
- 缓存配置:在
mybatis-config.xml中启用二级缓存:<settings> <setting name="cacheEnabled" value="true"/> <!-- 全局缓存开关 --> </settings> - 连接池优化:使用
type="POOLED"减少连接创建开销(见配置指南)。 - 批量操作:使用
SqlSession的批量方法提升插入/更新效率:
此方法减少数据库交互次数。try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (User user : userList) { mapper.insert(user); } session.commit(); // 批量提交 }
- 缓存配置:在
-
常见错误处理
- 映射错误:如
Invalid bound statement,检查namespace和 Mapper XML 路径是否一致。 - 事务问题:确保
<transactionManager type="JDBC">正确配置,并在操作后调用session.commit()。 - 日志调试:添加 Log4j 依赖,在配置中设置
<setting name="logImpl" value="LOG4J"/>查看 SQL 执行详情。
- 映射错误:如
五、总结
MyBatis 通过灵活的配置和强大的 SQL 控制,显著提升数据库开发效率。关键点包括:正确配置 mybatis-config.xml、使用动态 SQL 防范注入、利用缓存和批量操作优化性能。实践中,参考官方文档(https://mybatis.org/mybatis-3/zh/index.html)和单元测试能避免常见陷阱。
思维导图

工作原理和源码示例
MyBatis 工作原理详解与源码解析
一、MyBatis 工作原理
MyBatis 的核心工作流程分为四个阶段,通过 配置加载 → SQL 解析 → 执行调度 → 结果映射 实现数据库交互:
-
配置加载阶段
SqlSessionFactoryBuilder解析mybatis-config.xml,构建包含全局配置的Configuration对象- 加载 Mapper XML 文件,将 SQL 语句解析为
MappedStatement对象
-
SQL 解析阶段
- 根据方法调用定位对应的
MappedStatement SqlSource将参数转换为BoundSql对象(包含最终 SQL 和参数映射)
- 根据方法调用定位对应的
-
执行调度阶段
Executor调度StatementHandler创建 JDBCPreparedStatementParameterHandler将 Java 参数转换为 JDBC 类型并设置到 SQL 中
-
结果映射阶段
ResultSetHandler将ResultSet转换为 Java 对象TypeHandler处理 Java 类型与数据库类型的转换
关键设计:通过
SqlSession作为统一入口,内部委托给Executor实现具体操作
二、核心组件源码解析
-
Configuration- 功能:全局配置容器,存储所有
MappedStatement、类型处理器等 - 源码关键字段:
public class Configuration { protected final Map<String, MappedStatement> mappedStatements; protected final TypeHandlerRegistry typeHandlerRegistry; // 环境配置(数据源/事务) protected Environment environment; }
- 功能:全局配置容器,存储所有
-
Executor- 功能:SQL 执行调度器,包含缓存管理逻辑
- 执行流程:
public class CachingExecutor implements Executor { public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rb) { // 1. 检查二级缓存 // 2. 调用BaseExecutor.queryFromDatabase() } }
-
DefaultSqlSession- 功能:核心交互接口实现(占 MyBatis 80% 的 API 调用)
- 查询源码:
public class DefaultSqlSession implements SqlSession { public <E> List<E> selectList(String statement) { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, null, RowBounds.DEFAULT); } }
-
MappedStatement- 功能:封装 SQL 指令元数据(如 SQL 类型、参数映射)
- 存储结构:
public final class MappedStatement { private String id; // mapperId private SqlSource sqlSource; private SqlCommandType sqlCommandType; // SELECT/UPDATE等 }
三、常用注解详解
| 注解 | 作用 | 示例 |
|---|---|---|
@Select | 定义查询SQL | @Select("SELECT * FROM user WHERE id=#{id}") |
@Insert | 定义插入SQL | @Insert("INSERT INTO user(name) VALUES(#{name})") |
@Options | 设置执行选项 | @Options(useGeneratedKeys=true, keyProperty="id") |
@Param | 命名参数 | User findById(@Param("uid") int id) |
@Results | 结果集映射 |
@Results(id="userMap", value={
@Result(property="id", column="user_id"),
@Result(property="name", column="user_name")
})
四、核心组件功能与特点
| 组件 | 功能 | 特点 |
|---|---|---|
| SqlSession | 数据库交互入口 | 线程不安全,需每次请求创建新实例 |
| Executor | SQL执行调度 | 支持SIMPLE/REUSE/BATCH三种执行模式 |
| StatementHandler | JDBC Statement操作 | 实现预编译/参数设置 |
| ParameterHandler | 参数类型转换 | 使用TypeHandler处理Java→JDBC类型 |
| ResultSetHandler | 结果集转换 | 将ResultSet映射为Java对象集合 |
| TypeHandler | 数据类型桥接 | 内置70+种类型处理器(如Date↔TIMESTAMP) |
五、代码示例:完整查询流程
// 1. 初始化SqlSessionFactory
String config = "mybatis-config.xml";
InputStream input = Resources.getResourceAsStream(config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
// 2. 执行查询(使用注解Mapper)
try(SqlSession session = factory.openSession()) {
// 获取Mapper接口代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 实际调用流程:
// → DefaultSqlSession.getMapper()
// → MapperProxy.invoke()
// → Executor.query()
User user = mapper.findById(1);
System.out.println(user.getName());
}
// 注解Mapper接口
public interface UserMapper {
@Select("SELECT * FROM user WHERE id=#{id}")
@ResultMap("userMap")
User findById(int id);
}
六、高级特性源码解析
-
动态 SQL 实现
SqlNode树形结构解析<if>/<where>标签- 运行时根据参数动态拼接 SQL
public class IfSqlNode implements SqlNode { public boolean apply(DynamicContext context) { if (evaluator.evaluateBoolean(test, context.getBindings())) { contents.apply(context); return true; } return false; } } -
二级缓存机制
CachingExecutor装饰器模式实现- 缓存键生成规则:
MappedStatementId + SQL + 参数值
public class CachingExecutor implements Executor { private final TransactionalCacheManager cacheManager = new TransactionalCacheManager(); public <E> List<E> query(MappedStatement ms, Object param) { CacheKey key = createCacheKey(ms, param); return cacheManager.getObject(key, () -> delegate.query(ms, param)); } }
七、性能优化实践
-
批量操作优化
try(SqlSession session = factory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (int i=0; i<1000; i++) { mapper.insert(new User("user"+i)); if(i % 200 == 0) session.flushStatements(); // 分段提交 } session.commit(); } -
TypeHandler 自定义
@MappedTypes(PhoneNumber.class) public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber> { @Override public void setNonNullParameter(PreparedStatement ps, int i, PhoneNumber phone, JdbcType jt) { ps.setString(i, phone.toString()); // 自定义存储格式 } }
总结
MyBatis 的核心价值在于 SQL 与代码的解耦 和 灵活的映射能力,通过:
SqlSession作为统一入口协调组件Executor实现执行调度与缓存管理TypeHandler体系解决数据类型映射- 注解与 XML 双模式适配不同开发场景
掌握其工作原理和源码结构,可有效规避 N+1 查询、事务失效等常见问题,提升数据库操作效率。
思维导图


6462

被折叠的 条评论
为什么被折叠?



