1. MyBatis 整体架构设计解析
MyBatis 作为一款优秀的持久层框架,其架构设计体现了"大道至简"的哲学思想。整个框架的核心架构可分为三层:基础支撑层、核心处理层和接口层。这种分层设计使得各模块职责明确,耦合度低,便于扩展和维护。
1.1 基础支撑层
基础支撑层为整个框架提供最基础的通用功能支持,主要包括以下几个关键组件:
- 类型转换模块:处理 Java 类型与 JDBC 类型之间的转换。MyBatis 内置了丰富的类型处理器(TypeHandler),同时也支持自定义扩展。例如,当数据库中的 VARCHAR 类型映射到 Java 的 String 类型时,StringTypeHandler 就会发挥作用。
java复制public class StringTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
// 其他方法省略...
}
- 日志模块:提供统一的日志输出能力。MyBatis 采用适配器模式,可以无缝集成多种日志框架(如 SLF4J、Log4j2 等)。在配置文件中通过以下方式指定日志实现:
xml复制<settings>
<setting name="logImpl" value="LOG4J2"/>
</settings>
-
资源加载模块:封装了类加载器,提供统一的资源加载功能。这个模块主要处理 XML 配置文件、Mapper 接口等资源的加载工作,支持从类路径、文件系统或网络位置加载资源。
-
解析器模块:负责解析 XML 配置文件和注解配置。XPathParser 是核心解析器,它封装了 XPath 操作,简化了 XML 解析过程。解析过程中会使用到大量的 OGNL 表达式来处理动态 SQL。
1.2 核心处理层
核心处理层实现了 MyBatis 的核心功能,包括配置解析、SQL 解析、SQL 执行和结果集映射等关键功能。
1.2.1 配置解析
Configuration 类是 MyBatis 的核心配置类,采用单例模式管理所有配置信息。它包含了以下重要配置项:
- 环境配置(Environment)
- 类型处理器注册表(TypeHandlerRegistry)
- 插件拦截器链(InterceptorChain)
- Mapper 注册表(MapperRegistry)
配置解析过程主要发生在 XMLConfigBuilder 中,它会按照以下顺序解析 mybatis-config.xml 文件:
- 解析 properties 元素
- 解析 settings 元素
- 解析 typeAliases 元素
- 解析 plugins 元素
- 解析 objectFactory 元素
- 解析 objectWrapperFactory 元素
- 解析 environments 元素
- 解析 databaseIdProvider 元素
- 解析 mappers 元素
1.2.2 SQL 解析与执行
SQL 解析与执行是 MyBatis 最核心的功能,主要涉及以下组件:
-
SqlSource:表示映射文件或注解中定义的 SQL 语句,负责根据用户传入的参数生成可执行的 SQL。根据是否包含动态内容,分为 RawSqlSource 和 DynamicSqlSource。
-
SqlSession:核心接口,定义了数据库操作的基本方法。DefaultSqlSession 是其默认实现,通过 Executor 执行数据库操作。
-
Executor:SQL 执行器,负责 SQL 语句的执行和缓存维护。BaseExecutor 是抽象基类,定义了模板方法。具体实现类包括:
- SimpleExecutor:简单执行器,每次执行都会创建新的 Statement
- ReuseExecutor:重用执行器,重用预处理语句
- BatchExecutor:批处理执行器,支持批量操作
java复制public interface Executor {
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// 其他方法省略...
}
1.2.3 结果集映射
ResultSetHandler 负责将 JDBC 返回的 ResultSet 转换为 Java 对象。处理过程主要分为以下几步:
- 创建结果对象
- 根据 ResultMap 自动填充属性
- 处理嵌套映射(association 和 collection)
- 返回处理后的结果
MyBatis 支持多种结果映射策略,包括:
- 自动映射(基于列名与属性名的匹配)
- 显式映射(通过 resultMap 定义)
- 嵌套映射(处理复杂对象关系)
1.3 接口层
接口层提供了更简洁易用的 API,主要包括以下内容:
- SqlSessionFactory:创建 SqlSession 的工厂类,通常通过 SqlSessionFactoryBuilder 构建。建议使用单例模式管理 SqlSessionFactory。
java复制String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- Mapper 接口:通过动态代理技术,将接口方法调用转换为对应的 SQL 执行。MapperProxy 是代理类的核心实现,它通过 MapperMethod 将方法调用路由到适当的 SqlSession 方法。
java复制public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用处理逻辑
}
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MyBatis 核心流程分析
2.1 初始化流程
MyBatis 的初始化过程主要完成以下工作:
- 解析 mybatis-config.xml 配置文件,构建 Configuration 对象
- 解析 Mapper 接口和映射文件,注册 MappedStatement
- 创建 SqlSessionFactory 实例
关键代码路径:
java复制SqlSessionFactoryBuilder.build()
-> XMLConfigBuilder.parse()
-> configurationElement() // 解析配置文件的各个部分
-> new DefaultSqlSessionFactory(config)
注意事项:初始化过程是相对耗时的操作,应该避免重复创建 SqlSessionFactory。建议在应用启动时完成初始化,并通过单例模式管理 SqlSessionFactory。
2.2 SQL 执行流程
一条 SQL 语句的执行大致经历以下步骤:
- 获取 SqlSession 实例
- 获取 Mapper 接口的代理对象
- 调用 Mapper 方法
- 根据方法签名查找对应的 MappedStatement
- 创建 BoundSql 对象(包含最终要执行的 SQL 和参数映射)
- 创建 CacheKey(用于二级缓存)
- 执行查询并处理结果集
mermaid复制graph TD
A[SqlSession] --> B[MapperProxy]
B --> C[MapperMethod]
C --> D[SqlSession]
D --> E[Executor]
E --> F[StatementHandler]
F --> G[ParameterHandler]
F --> H[ResultSetHandler]
2.3 插件机制原理
MyBatis 的插件机制基于动态代理和责任链模式实现,可以拦截以下四大对象的方法调用:
- Executor (update, query, flushStatements, commit, rollback 等方法)
- StatementHandler (prepare, parameterize, batch, update, query 等方法)
- ParameterHandler (getParameterObject, setParameters 等方法)
- ResultSetHandler (handleResultSets, handleOutputParameters 等方法)
实现自定义插件的步骤:
- 实现 Interceptor 接口
- 使用 @Intercepts 注解指定要拦截的方法
- 在配置文件中注册插件
java复制@Intercepts({
@Signature(type= Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 前置处理
Object result = invocation.proceed();
// 后置处理
return result;
}
}
3. 高级特性解析
3.1 缓存机制
MyBatis 提供两级缓存机制:
一级缓存:
- 作用范围:SqlSession 级别
- 默认开启,无法关闭
- 执行 update/commit/rollback 操作时会清空缓存
二级缓存:
- 作用范围:Mapper 级别(namespace)
- 需要手动配置开启
- 可配置各种淘汰策略和刷新间隔
配置二级缓存:
xml复制<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
注意事项:在使用分布式环境时,默认的二级缓存实现(PerpetualCache)可能不适用,建议集成 Redis 等分布式缓存解决方案。
3.2 动态 SQL
MyBatis 提供强大的动态 SQL 功能,主要元素包括:
- if:条件判断
- choose/when/otherwise:多条件选择
- trim/where/set:辅助处理 SQL 片段
- foreach:循环遍历集合
xml复制<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
3.3 延迟加载
MyBatis 支持关联对象的延迟加载,可以显著提高查询性能。配置方式:
xml复制<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id"
javaType="Author" select="selectAuthor"/>
</resultMap>
4. 性能优化实践
4.1 常见性能问题
- N+1 查询问题:解决方法是使用关联查询或批量加载
- 大结果集内存溢出:可以使用分页查询或结果集流式处理
- 缓存滥用:合理设置缓存大小和淘汰策略
4.2 优化建议
- 合理使用批量操作:
java复制try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
Mapper mapper = session.getMapper(Mapper.class);
for (int i = 0; i < 1000; i++) {
mapper.insert(data);
}
session.commit();
}
- 使用存储过程处理复杂逻辑:
xml复制<select id="callProcedure" statementType="CALLABLE">
{call my_procedure(#{param1,mode=IN,jdbcType=INTEGER})}
</select>
- 合理配置连接池参数(如最大连接数、超时时间等)
4.3 监控与诊断
- 开启慢查询日志:
xml复制<settings>
<setting name="defaultStatementTimeout" value="30"/>
</settings>
- 使用插件记录 SQL 执行时间:
java复制@Intercepts(@Signature(type= StatementHandler.class,
method = "query",
args = {Statement.class, ResultHandler.class}))
public class SqlCostTimeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long costTime = System.currentTimeMillis() - startTime;
// 记录耗时
return result;
}
}
5. 扩展与集成
5.1 与 Spring 集成
MyBatis-Spring 项目提供了与 Spring 框架的无缝集成:
- 配置 SqlSessionFactoryBean:
xml复制<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:mapper/**/*.xml"/>
</bean>
- 使用 MapperScannerConfigurer 自动扫描 Mapper 接口:
xml复制<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
</bean>
5.2 自定义类型处理器
实现 TypeHandler 接口处理特殊类型转换:
java复制@MappedTypes(PhoneNumber.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class PhoneNumberTypeHandler extends BaseTypeHandler<PhoneNumber> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
PhoneNumber parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.getValue());
}
// 其他方法省略...
}
5.3 多数据源支持
通过 AbstractRoutingDataSource 实现动态数据源切换:
java复制public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
// 使用注解切换数据源
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "master";
}
在实际项目中,MyBatis 的架构设计展现了良好的扩展性和灵活性。通过深入理解其内部机制,开发者可以更好地应对各种复杂场景,构建高性能的数据访问层。
