当前位置: 首页 > news >正文

MyBatis 核心组件剖析:架构、协作与源码解读

MyBatis 作为一款经典的持久层框架,其设计精妙之处在于通过几个核心组件的协作,将 SQL 操作与 Java 对象优雅地结合起来。本文将深入剖析 MyBatis 的核心组件,包括它们的作用、相互关系以及底层实现原理。

1.MyBatis 核心组件概览

MyBatis 的核心组件主要包括以下几个部分:

  1. SqlSessionFactoryBuilder:负责从 XML 配置文件或 Java 代码中构建 SqlSessionFactory。
  2. SqlSessionFactory:工厂模式的实现,负责创建 SqlSession 实例。
  3. SqlSession:提供了执行 SQL 命令的方法,是应用与 MyBatis 之间的主要编程接口。
  4. Executor:SqlSession 内部使用 Executor 来执行 SQL 语句。
  5. Mapper 接口与映射文件:定义 SQL 语句与 Java 方法的映射关系。
  6. TypeHandler:负责 Java 类型与 JDBC 类型之间的转换。
  7. ParameterHandler:处理 SQL 参数。
  8. ResultSetHandler:处理 SQL 查询结果集。

这些组件相互协作,形成了 MyBatis 的核心架构。下面我们通过一个整体架构图来直观地了解它们之间的关系:

+-------------------+     +-------------------+     +-------------------+
|                   |     |                   |     |                   |
|  SqlSessionFactory |<--->|     SqlSession    |<--->|      MapperProxy  |
|                   |     |                   |     |                   |
+-------------------+     +-------------------+     +-------------------+^                      |     ^                      ^|                      |     |                      ||                      v     |                      |
+-------------------+     +-------------------+     +-------------------+
|                   |     |                   |     |                   |
|    Configuration  |     |     Executor      |     |   MapperRegistry  |
|                   |     |                   |     |                   |
+-------------------+     +-------------------+     +-------------------+^                      |     ^|                      |     ||                      v     |
+-------------------+     +-------------------+
|                   |     |                   |
|   MappedStatement |     |    TypeHandler    |
|                   |     |                   |
+-------------------+     +-------------------+

2.核心组件详解

1. SqlSessionFactoryBuilder

作用:SqlSessionFactoryBuilder 是 MyBatis 的入口点,负责解析配置文件并构建 SqlSessionFactory 实例。

源码关键代码

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 解析 XML 配置文件XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);// 构建 Configuration 对象return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {// 创建 DefaultSqlSessionFactory 实例return new DefaultSqlSessionFactory(config);}
}

使用示例

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2. SqlSessionFactory

作用:SqlSessionFactory 是一个工厂接口,负责创建 SqlSession 实例。它是线程安全的,可以被多个线程共享。

核心方法

  • openSession():创建一个新的 SqlSession 实例。
  • openSession(boolean autoCommit):创建一个带有自动提交功能的 SqlSession。
  • openSession(ExecutorType execType):创建一个指定执行器类型的 SqlSession。

源码关键代码

public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);// 其他重载方法...
}public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 创建执行器final Executor executor = configuration.newExecutor(tx, execType);// 创建 DefaultSqlSession 实例return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
}

3. SqlSession

作用:SqlSession 是 MyBatis 的核心接口,提供了执行 SQL 命令的方法。它是线程不安全的,应该在方法内部使用,用完后及时关闭。

核心方法

  • selectOne(String statement, Object parameter):查询单个结果。
  • selectList(String statement, Object parameter):查询多个结果。
  • insert(String statement, Object parameter):插入数据。
  • update(String statement, Object parameter):更新数据。
  • delete(String statement, Object parameter):删除数据。
  • commit():提交事务。
  • rollback():回滚事务。
  • getMapper(Class<T> type):获取 Mapper 接口的代理对象。

源码关键代码

public interface SqlSession extends Closeable {<T> T selectOne(String statement);<T> T selectOne(String statement, Object parameter);<E> List<E> selectList(String statement);<E> List<E> selectList(String statement, Object parameter);int insert(String statement);int insert(String statement, Object parameter);int update(String statement);int update(String statement, Object parameter);int delete(String statement);int delete(String statement, Object parameter);void commit();void commit(boolean force);void rollback();void rollback(boolean force);<T> T getMapper(Class<T> type);Configuration getConfiguration();Connection getConnection();
}public class DefaultSqlSession implements SqlSession {private final Configuration configuration;private final Executor executor;private final boolean autoCommit;private boolean dirty;public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.autoCommit = autoCommit;this.dirty = false;}@Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}@Overridepublic int insert(String statement, Object parameter) {return update(statement, parameter);}@Overridepublic int update(String statement, Object parameter) {try {dirty = true;// 通过执行器执行更新操作MappedStatement ms = configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}@Overridepublic <T> T getMapper(Class<T> type) {// 通过 Configuration 获取 Mapper 代理return configuration.getMapper(type, this);}
}

4. Executor

作用:Executor 是 MyBatis 的执行器,负责 SQL 语句的执行和缓存的维护。

主要实现类

  • SimpleExecutor:简单执行器,每次执行都会创建新的预处理语句。
  • ReuseExecutor:可重用执行器,会重用预处理语句。
  • BatchExecutor:批处理执行器,用于批量操作。
  • CachingExecutor:缓存执行器,用于二级缓存的管理。

源码关键代码

public interface Executor {ResultHandler NO_RESULT_HANDLER = null;int update(MappedStatement ms, Object parameter) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;List<BatchResult> flushStatements() throws SQLException;void commit(boolean required) throws SQLException;void rollback(boolean required) throws SQLException;CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);boolean isCached(MappedStatement ms, CacheKey key);void clearLocalCache();void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);Transaction getTransaction();void close(boolean forceRollback);boolean isClosed();void setExecutorWrapper(Executor executor);
}public abstract class BaseExecutor implements Executor {// 实现 Executor 接口的方法// 包含事务管理、缓存管理等通用逻辑
}public class SimpleExecutor extends BaseExecutor {@Overridepublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);// 准备语句stmt = prepareStatement(handler, ms.getStatementLog());// 执行更新return handler.update(stmt);} finally {closeStatement(stmt);}}@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 准备语句stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}}
}

5. Mapper 接口与映射文件

作用:Mapper 接口定义了数据库操作的方法,映射文件(或注解)定义了这些方法对应的 SQL 语句。

映射文件示例

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper"><select id="getUserById" parameterType="int" resultType="com.example.entity.User">SELECT * FROM users WHERE id = #{id}</select><insert id="insertUser" parameterType="com.example.entity.User">INSERT INTO users (username, email, age)VALUES (#{username}, #{email}, #{age})</insert><!-- 其他 SQL 映射... -->
</mapper>

Mapper 接口示例

package com.example.mapper;import com.example.entity.User;
import java.util.List;public interface UserMapper {User getUserById(int id);void insertUser(User user);void updateUser(User user);void deleteUser(int id);List<User> getAllUsers();
}

MapperProxy 实现

MyBatis 使用动态代理实现 Mapper 接口:

public class MapperProxy<T> implements InvocationHandler, Serializable {private static final long serialVersionUID = -642454039855972983L;private final SqlSession sqlSession;private final Class<T> mapperInterface;private final Map<Method, MapperMethodInvoker> methodCache;public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;this.methodCache = methodCache;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return methodCache.computeIfAbsent(method, m -> {if (m.isDefault()) {try {if (privateLookupInMethod == null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));} else {return new DefaultMethodInvoker(getMethodHandleJava9(method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException| NoSuchMethodException e) {throw new RuntimeException(e);}} else {// 创建 MapperMethod 实例return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}});} catch (RuntimeException re) {Throwable cause = re.getCause();throw cause == null ? re : cause;}}
}

3.核心组件协作流程

下面通过一个查询操作的时序图,展示 MyBatis 核心组件的协作流程:

Client                SqlSession        Executor    MappedStatement    JDBC|                      |                |              |               ||  getUserById(1)      |                |              |               ||--------------------->|                |              |               ||                      |  getMappedStatement("...") |               ||                      |---------------------------->|               ||                      |                |              |               ||                      |  query(ms, 1)  |              |               ||                      |-------------->|              |               ||                      |                |  getBoundSql() |               ||                      |                |------------->|               ||                      |                |              |               ||                      |                |  prepareStatement()          ||                      |                |----------------------------->||                      |                |              |  Connection   ||                      |                |              |<-------------||                      |                |              |               ||                      |                |  executeQuery()              ||                      |                |----------------------------->||                      |                |              |  ResultSet    ||                      |                |<-------------|               ||                      |                |              |               ||                      |                |  handleResultSets()          ||                      |                |<-------------|               ||                      |<---------------|              |               ||<---------------------|                |              |               |

4.总结

通过深入剖析 MyBatis 的核心组件,我们可以看到其设计的精妙之处:

  1. 工厂模式:SqlSessionFactoryBuilder 构建 SqlSessionFactory,SqlSessionFactory 创建 SqlSession。
  2. 代理模式:MapperProxy 实现 Mapper 接口的动态代理,将方法调用转换为 SQL 执行。
  3. 策略模式:Executor 提供多种执行策略(SimpleExecutor、ReuseExecutor、BatchExecutor)。
  4. 模板方法模式:BaseExecutor 实现了通用的执行逻辑,具体实现由子类完成。

这种设计使得 MyBatis 既保持了灵活性,又提供了简单易用的 API。开发者可以通过配置文件或注解定义 SQL 映射,然后通过 Mapper 接口进行数据库操作,无需编写繁琐的 JDBC 代码。

在实际开发中,理解 MyBatis 的核心组件和工作原理,有助于我们更好地使用 MyBatis 进行开发,也能够在遇到问题时更快地定位和解决问题。

相关文章:

  • NLP学习路线图(八):常见算法-线性回归、逻辑回归、决策树
  • AI时代新词-Transformer架构:开启AI新时代的关键技术
  • rpm安装jenkins-2.452
  • 深度解析 vm.max_map_count:用途、原理与调优建议
  • 如何用ChatGPT提升学术长文质量
  • VR 技术在农业领域或许是一抹新曙光​
  • VR 展厅开启一场穿越时空的邂逅​
  • Java 访问者模式深度重构:从静态类型到动态行为的响应式设计实践
  • 力扣HOT100之回溯:22. 括号生成
  • 基于cornerstone3D的dicom影像浏览器 第二十三章 mpr预设窗值与vr preset
  • 仓储物流场景下国标GB28181视频平台EasyGBS视频实时监控系统应用解决方案
  • 互联网大厂Java求职面试:AI与大模型应用集成中的架构难题与解决方案-2
  • [BUG]Debian/Linux操作系统中 安装 curl等软件显示无候选安装(E: 软件包 curl 没有可安装候选)
  • noc多核芯片设计:booksim仿真从入门到精通2Router 类型及路由算法修改
  • MPI与多线程(如OpenMP)混合编程注意事项与性能优化
  • 【运维】Zerotier删除节点后的恢复操作指南
  • 【登录优化】redis删除旧token
  • PLC 与变频器通讯接线与控制技巧
  • 深入Linux网络栈:套接字接口工作机制与端到端通信开发
  • 无法访问Docker官网,国内如何合规高效安装Docker软件
  • 医院推广营销方式/seo免费软件
  • 证件查询网入口/seo查询百科
  • 福州高端网站制作/百度域名注册
  • 自建b2b代表网站/爱站网的关键词是怎么来的
  • 有百度推广的网站/平台优化是什么意思
  • 沧州网站建设公司/网络营销策划的基本原则