作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题
代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等
回答
Executor
执行器是 Mybatis 核心组件,它负责执行 SQL 语句并处理数据库的交互,主要包括 SQL 语句的生成、参数绑定、执行、缓存处理以及结果集的映射等。
Mybatis 内置了三种基本的Executor
。
SimpleExecutor
:每执行一次 update 或 select,就开启一个Statement
对象,用完立刻关闭 Statement 对象。ReuseExecutor
:执行 update 或 select,以 sql 作为 key 查找Statement
对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement>内,供下一次使用。BatchExecutor
:支持批处理的执行器,支持批量更新操作,依赖 JDBC 的批处理。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// ExecutorType.SIMPLE 为默认执行器
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
// 批量执行器
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
// 复用执行器
executor = new ReuseExecutor(this, transaction);
} else {
// 简答执行器
executor = new SimpleExecutor(this, transaction);
}
// 缓存执行器
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Executor 工作原理
- 获取 MappedStatement,每个 Mapper 方法在 MyBatis 配置中都有对应的
MappedStatement
,它包含了 SQL 语句和配置信息。
// Map 缓存所有的 MappedStatement
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
// 根据全限定名得到 MappedStatement
MappedStatement ms = configuration.getMappedStatement(statementId);
- 生成 BoundSql:基于
MappedStatement
生成BoundSql
,包含了实际执行的 SQL 语句和参数。
BoundSql boundSql = ms.getBoundSql(parameterObject);
public BoundSql getBoundSql(Object parameterObject) {
// 从SqlSource得到BoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 获取 ParameterMapping
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
- 创建 ParameterHandler:负责将方法参数绑定到 SQL 语句的占位符中。
ParameterHandler parameterHandler = configuration.newParameterHandler(ms, parameterObject, boundSql);
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// 创建 ParameterHandler 对象
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 插件执行
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public class DefaultParameterHandler implements ParameterHandler {
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// BoundSql 的参数映射列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 自定义 TypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 参数设置
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
- 执行 SQL 语句:根据
Executor
的不同实现,执行 SQL 语句。
public <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
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 预编译
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行 SQL 查询
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 获取待执行 SQL
String sql = boundSql.getSql();
// SQL 执行
statement.execute(sql);
// 结果集处理
return resultSetHandler.handleResultSets(statement);
}
- 处理结果集:通过
**ResultHandler**
进行处理和映射,转换为相应的 Java 对象。
public class DefaultResultSetHandler implements ResultSetHandler {
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 结果集包装器
ResultSetWrapper rsw = getFirstResultSet(stmt);
// Mapper结果映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// ResultMap 映射校验
validateResultMapsCount(rsw, resultMapCount);
// 遍历映射结果集
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// JDBC ResultSet 映射处理
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
}
扩展
除了上述三种执行器,Mybatis还内置了 CachingExecutor,它通过二级缓存机制实现对数据库查询结果的缓存,适用于频繁读取、数据相对静态的场景。
CachingExecutor
CachingExecutor
是 MyBatis 实现二级缓存的执行器。它通过装饰模式在 Executor
的基础上增加了缓存功能,在查询数据库之前先检查缓存,如果缓存中有数据,则直接返回缓存中的数据,否则执行查询并将结果存入缓存。
- 提升性能。使用二级缓存,显著减少对数据库的访问次数。当数据已经缓存在内存中时,直接从缓存中读取,无需再次访问数据库。
- 数据一致性。执行更新操作、提交和回滚事务时会清空相关缓存,以确保缓存中的数据与数据库中的数据保持一致。通过
flushCacheIfRequired
方法来实现。 - 事务支持。事务提交时,将事务中的缓存操作应用到二级缓存中;在事务回滚时,撤销未提交的缓存操作。通过
TransactionalCacheManager
管理事务中的缓存操作。