MyBatis 核心组件剖析:架构、协作与源码解读
MyBatis 作为一款经典的持久层框架,其设计精妙之处在于通过几个核心组件的协作,将 SQL 操作与 Java 对象优雅地结合起来。本文将深入剖析 MyBatis 的核心组件,包括它们的作用、相互关系以及底层实现原理。
1.MyBatis 核心组件概览
MyBatis 的核心组件主要包括以下几个部分:
- SqlSessionFactoryBuilder:负责从 XML 配置文件或 Java 代码中构建 SqlSessionFactory。
- SqlSessionFactory:工厂模式的实现,负责创建 SqlSession 实例。
- SqlSession:提供了执行 SQL 命令的方法,是应用与 MyBatis 之间的主要编程接口。
- Executor:SqlSession 内部使用 Executor 来执行 SQL 语句。
- Mapper 接口与映射文件:定义 SQL 语句与 Java 方法的映射关系。
- TypeHandler:负责 Java 类型与 JDBC 类型之间的转换。
- ParameterHandler:处理 SQL 参数。
- 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 的核心组件,我们可以看到其设计的精妙之处:
- 工厂模式:SqlSessionFactoryBuilder 构建 SqlSessionFactory,SqlSessionFactory 创建 SqlSession。
- 代理模式:MapperProxy 实现 Mapper 接口的动态代理,将方法调用转换为 SQL 执行。
- 策略模式:Executor 提供多种执行策略(SimpleExecutor、ReuseExecutor、BatchExecutor)。
- 模板方法模式:BaseExecutor 实现了通用的执行逻辑,具体实现由子类完成。
这种设计使得 MyBatis 既保持了灵活性,又提供了简单易用的 API。开发者可以通过配置文件或注解定义 SQL 映射,然后通过 Mapper 接口进行数据库操作,无需编写繁琐的 JDBC 代码。
在实际开发中,理解 MyBatis 的核心组件和工作原理,有助于我们更好地使用 MyBatis 进行开发,也能够在遇到问题时更快地定位和解决问题。