单独使用 MyBatis执行数据库的操作时,常见代码如下:
InputStream is = Resources.getResourceAsStream("spring/mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
User user = session.selectOne("org.xxx.dao.IUserDao.queryUserInfoById", 1L);
在上一篇构建流程中,已经介绍了 SqlSessionFactoryBuilder 构建部分,接下来解析 SQL执行部分。构建完成后,生成了 SqlSession工厂(SqlSessionFactory),SqlSession工厂用来生成 SqlSession,SqlSession 主要用来执行命令,获取映射,管理事务。
下面先来看下 SqlSessionFactory。
SqlSessionFactory 是一个接口,默认实现为 DefaultSqlSessionFactory。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
// 生成SqlSession
public SqlSession openSession() {
// 传入defaultExecutorType,为ExecutorType.SIMPLE
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// 生成SqlSession
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);
// 新建执行器,执行器负责SQL语句的生成以及查询缓存的维护,默认是SimpleExecutor,使用的是Configuration中的newExecutor方法
final Executor executor = configuration.newExecutor(tx, execType);
// 生成SqlSession,为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();
}
}
}
// Configuration类中新建执行器
public class Configuration {
// 新建执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 如果未设置执行器类型,则默认为简单类型
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 = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
新建执行器大致流程为:
// SQL会话,它是MyBatis的核心API, 主要用来执行命令, 获取映射, 管理事务, 接收开发人员提供的statement和参数, 执行并返回操作结果
public interface SqlSession extends Closeable {
// 查询单个
<T> T selectOne(String statement, Object parameter);
// 查询集合
<E> List<E> selectList(String statement, Object parameter);
// 查询map
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
// 查询游标
<T> Cursor<T> selectCursor(String statement);
...
}
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
// 查询单个
@Override
public <T> T selectOne(String statement, Object parameter) {
// 根据statement和参数查询集合
List<T> list = this.<T>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;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据statement id找到对应的mappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 使用执行器查询结果,rowBounds参数不用管,是用来逻辑分页的,ResultHandler参数是null
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
...
}
我们需要创建一个 SQL会话来执行 CRUD 操作,MyBatis 中指的就是 SqlSession 接口,其中的方法定义了 CRUD 操作,默认实现类为 DefaultSqlSession。以查询集合 selectList 方法为例进行简单解析如下:
执行器 Executor 是一个接口,定义了数据库操作的基本方法。有实现类 CachingExecutor 和抽象实现类 BaseExecutor,之前新建执行器是三种执行器 SimpleExecutor、BatchExecutor、ReuseExecutor 都继承了 BaseExecutor,UML类图如下:
下面分析执行器中 query 方法的源码。
在新建执行器流程中,默认开启缓存,我们先看一下 CachingExecutor 中的 query 方法。
public class CachingExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 步骤1:获取 BoundSql 对象,解析 BoundSql
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建查询语句对应的CacheKey对象
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 获取查询语句所在命名空间对应的二级缓存
Cache cache = ms.getCache();
// 步骤2:是否开启了二级缓存功能,默认情况下是没有开启缓存的(二级缓存),如果要开启二级缓存,需要在SQL映射文件中添加一行
if (cache != null) {
// 根据
flushCacheIfRequired(ms);
// 检测 SQL 节点的 useCache 配置以及是否使用了 resultHandler 配置
if (ms.isUseCache() && resultHandler == null) {
//步骤3: 二级缓存不能保存输出类型的参数 如果查询操作调用了包含输出参数的存储过程,则报错
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
// 步骤4:查询二级缓存
// 调用TransactionalCacheManager.getObject()方法查询二级缓存
List<E> list = (List<E>) tcm.getObject(cache, key);
// 如果缓存中不存在结果
if (list == null) {
// 步骤5:二级缓存没用相应的结果对象,调用封装的 Executor 对象的 query() 方法,这个 query() 方法会先查询一级缓存
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 步骤6:将查询结果保存到 TransactionalCache.entriesToAddOnCommit 集合中
tcm.putObject(cache, key, list);
}
return list;
}
}
// 没有启动二级缓存,直接调用底层 Executor 执行数据数据库查询操作
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
...
}
CachingExecutor 查询源码流程如下:
在 CachingExecutor 中的 query 源码中,未开启二级缓存或者二级缓存未查到时,都会调用 BatchExecutor 的 query 方法去查询,下面我们来看一下基础执行器的源码:
public abstract class BaseExecutor implements Executor {
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
// 如果事务已经关闭,抛出异常
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 非嵌套查询,并且
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 增加查询层数
queryStack++;
// 查询一级缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 针对存储过程调用的处理,其功能是:在一级缓存命中时,获取缓存中保存的输出类型参数,并设置到用户传入的实参(parameter)对象中
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 未查到则去数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
// 当前查询完成,查询层数减少
queryStack--;
}
// 在最外层的查询结束时,所有嵌套查询也已经完全加载,所以这里可以触发 DeferredLoad 加载一级缓存中记录的嵌套查询的结果对象
if (queryStack == 0) {
// 加载 延迟加载队列中所有元素,deferredLoads:延迟加载队列
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// 清空延迟加载队列
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// 生命周期为 STATEMENT,清除一级缓存
clearLocalCache();
}
}
return list;
}
// 从数据库查询数据
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 标识 sql 查询
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 查询数据库,在具体的执行器中实现
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 移除标识
localCache.removeObject(key);
}
// 将结果放入一级缓存
localCache.putObject(key, list);
// 存储过程
if (ms.getStatementType() == StatementType.CALLABLE) {
// 存储过程中 出参类型缓存
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
...
}
public class SimpleExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 获取配置对象configuration
Configuration configuration = ms.getConfiguration();
// 新建StatementHandler,它负责操作Statement对象与数据库进行交互
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备语句,这里就是原生JDBC代码创建 Statement 的过程
stmt = prepareStatement(handler, ms.getStatementLog());
// 调用 StatementHandler.query() 方法,执行 SQL 语句,并通过 ResultSetHandler 完成结果集映射
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
...
}
BaseExecutor 查询源码流程如下:
StatementHandler 是一个接口,依赖 ParameterHandler 和 ResultSetHandler,定义了一些规范,有四个实现类,定义了具体的实现。
StatementHandler 是对 Statement 的一个封装,提供了 Statement 的所有功能,包括绑定执行的实参,批量执行 Sql语句,执行 select、update、delete、insert 语句功能,还有将结果集映射为成结果对象。
Statement 主要用于 sql 语句的执行和 sql 执行返回结果的处理。
源码如下:
public interface StatementHandler {
// 从连接中获取一个 Statement
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 绑定 statement 执行时所需的实参
void parameterize(Statement statement)
throws SQLException;
// 批量执行 SQL 语句
void batch(Statement statement)
throws SQLException;
// 执行 update/insert/delete 语句
int update(Statement statement)
throws SQLException;
// 执行 select 语句
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
// 执行 select 语句
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 获取 BoundSql 对象
BoundSql getBoundSql();
// 获取 ParameterHandler 对象
ParameterHandler getParameterHandler();
}
UML类图如下:
RoutingStatementHandler : 定义一个 StatementHandler 接口 代理对象,根据 statementType 类型,创建 StatementHandler 接口具体实现。在MyBatis工作时,使用的 StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
BaseStatementHandler : 是实现 StatementHandler 接口的一个抽象类,用于简化 StatementHandler 接口实现的难度,它主要有三个子类:
这里注意一下,SimpleStatementHandler 和 PreparedStatementHandler 的区别是 SQL 语句是否包含变量,是否通过外部进行参数传入。
SimpleStatementHandler: 用于执行没有任何参数传入的 SQL。
PreparedStatementHandler :需要对外部传入的变量和参数进行提前参数绑定和赋值。
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
// 对象工厂,创建对象来使用
protected final ObjectFactory objectFactory;
// 类型转换注册器
protected final TypeHandlerRegistry typeHandlerRegistry;
// SQL 查询结果,封装成 ResultMap 对象 处理器
protected final ResultSetHandler resultSetHandler;
// 记录使用的 ParameterHandler 对象, ParameterHandler 的主要功能是为 SQL 句绑定实参 ,使用实参替换 SQL 语句的中 ? 占位符。
protected final ParameterHandler parameterHandler;
// sql 执行器
protected final Executor executor;
// 封装 mapper.xml 节点 成 MappedStatement 对象
protected final MappedStatement mappedStatement;
// RowBounds 记录了用户设置的 offset limit ,用于在结果采集器中定位映射的起始位置和结束位置
protected final RowBounds rowBounds;
// 数据库可以执行的 SQL 语句
protected BoundSql boundSql;
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) {
// 调用 KeyGenerator processBefore() 方法获取主键
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
}
SimpleStatementHandler 继承了 BaseStatementHandler 抽象类。它底层使用 java.sql.Statement 对象来完成数据库的相关操作。所以 SQL 语句中不能存在占位符。
public class SimpleStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 获取待执行的 SQL 语句
String sql = boundSql.getSql();
// 执行 SQL 语句
statement.execute(sql);
// 获取结果集 ResultSet,对结果集进行处理
return resultSetHandler.<E>handleResultSets(statement);
}
@Override
public void parameterize(Statement statement) throws SQLException {
// N/A
}
...
}
PreparedStatementHandler 底层依赖于 java.sql.PreparedStatement 对象来完成数据库的相关操作。在 SimpleStatementHandler.parameterize() 方法中, 会调用前面介绍的 ParameterHandler.setParameters() 方法完成 SQL 语句的参数绑定。
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 类型强制转换
PreparedStatement ps = (PreparedStatement) statement;
// 执行 SQL 语句
ps.execute();
// 获取结果集 ResultSet,对结果集进行处理
return resultSetHandler.<E> handleResultSets(ps);
}
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 获取待执行的 SQL 语句
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
// 根据 MappedStatement.keyGenerator 字段的位,创建 PreparedStatement 对象
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
// 在 insert 语句执行完成之后,会将 keyColumn 指定的列对象
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
// 设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集采集器是否可更新
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
// 创建普通的 PreparedStatement 对象
return connection.prepareStatement(sql);
}
}
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
...
}
CallableStatementHndler 底层依赖于 java.sql.CallableStatement 调用存储过程,其中 parameterize()方法也会调用 ParameterHandler.setParameters() 方法完 SQL 语句参数绑定,并指定输出参数的索引位置和 JDBC 类型。
public class CallableStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
@Override
public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}
//处理存储过程中的参数类型
private void registerOutputParameters(CallableStatement cs) throws SQLException {
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0, n = parameterMappings.size(); i < n; i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
if (null == parameterMapping.getJdbcType()) {
throw new ExecutorException("The JDBC Type must be specified for output parameter. Parameter: " + parameterMapping.getProperty());
} else {
if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
} else {
if (parameterMapping.getJdbcTypeName() == null) {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
} else {
cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());
}
}
}
}
}
}
}
综上所述,在第一章中调用到了 StatementHandler 的 query 方法,因为 StatementHandler 是一个接口,所以真正调用到的是 RoutingStatementHandler 的 query 方法,由于有传入参数,会调用到 PreparedStatementHandler 中的 query 方法,流程如下:
ParameterHandler 是一个 SQL 实参处理器,主要功能是为 SQL 语句绑定实参,使用传入的实参替换 SQL 语句的中 ? 占位符。
public interface ParameterHandler {
Object getParameterObject();
// 设置参数
void setParameters(PreparedStatement ps)
throws SQLException;
}
DefaultParameterHandler 是 ParameterHandler 接口唯一默认实现类。
在 BoundSql 中记录的 SQL 语句中可能包含 “?” 占位符,而每个 “?” 占位符都对应了 BoundSql.parameterMappings 集合中的一个元素,在该 ParameterMapping 中记录了对应的参数名称以及该参数的相关属性。
public class DefaultParameterHandler implements ParameterHandler {
// TypeHandlerRegistry 对象,管理 MyBatis 中的 全部 TypeHandler 对象
private final TypeHandlerRegistry typeHandlerRegistry;
// MappedSt tement 对象,其中记录 SQL 节点相应的配置信息
private final MappedStatement mappedStatement;
// 用户传入的实参对象
private final Object parameterObject;
// 包含 "?" 占位符的,包含数据库将要执行的 sql 语句的对象
private final BoundSql boundSql;
private final Configuration configuration;
...
/**
* 将 BoundSql 中,含有 "?" 占位符替换为实参。
* setParameters 会遍历 BoundSql.parameterMappings 集合中记录的 ParameterMapping 对象,井根据其中记录的参数名称查找相应实参 然后与 SQL 语句绑定。
*/
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 取出 sql 中的参数映射列表
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)) {
// 获取参数名称
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 整个实参为空
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 实参可以直接通过 TypeHandler 转换成 JdbcType
value = parameterObject;
} else {
// 获取对象中相应的属性位或查找 Map 对象中位
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取 ParameterMapping 中设置的 TypeHandler 对象
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 通过 TypeHandler setParameter() 方法会调用 PreparedStatement.set *() 方法,为 SQL 语句绑定相应的实参
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
ResultSetHandler 是一个接口,主要有两个功能,处理 Statement 执行后产生的结果集,生成结果列表,以及处理存储过程执行后的输出参数。
public interface ResultSetHandler {
// 处理集合结果集
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 处理游标结果集
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 处理存储过程执行后的输出参数
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
DefaultResultSetHandler 是 ResultSetHandler 的默认实现类,它按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。
之前分析到通过 JDBC 获取到结果集 ResultSet 了,需要进一步做结果集的类型处理,下面来看处理结果集的源码:
public class DefaultResultSetHandler implements ResultSetHandler {
// 处理集合结果集
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// 获取第一个结果集,将传统JDBC的 ResultSet 包装成一个包含结果列元信息的 ResultSetWrapper 对象,这里获取了结果集并且包装成相应的包装类
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取所有要映射的ResultMap(按照逗号分割出来的)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 要映射的ResultMap的数量
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 循环处理每个ResultMap,一般只有一个
while (rsw != null && resultMapCount > resultSetCount) {
// 得到结果映射信息
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集,从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到 multipleResults中
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);
}
// 处理结果集
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 如果没有 resultHandler,新建 DefaultResultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 调用自己的 handleRowValues
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 得到记录的list
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 如果有 resultHandler,调用 handleRowValues
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// 最后关闭结果集
closeResultSet(rsw.getResultSet());
}
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 是否有内嵌,这里不用管
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 继续处理
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// 获取结果集信息,使用rowBounds的分页信息,进行逻辑分页(即在内存中分页)
skipRows(rsw.getResultSet(), rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
// 通过标签的子标签对结果映射进行鉴别
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
// 将查询结果封装到POJO中,rsw 是结果集的包装类,里面封装了返回结果的信息,通过结果集包装类创建 POJO
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 处理对象嵌套的映射关系
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
}
大致流程如下:
getRowValue 方法就是通过结果集包装类和一开始定义的类字段( POJO 中的字段) 封装成一个 Object 实体类。
// 取一行的值
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
// 实例化ResultLoaderMap(延迟加载器)
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 调用自己的createResultObject,内部就是new一个对象(如果是简单类型,new完也把值赋进去)
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 一般不是简单类型不会有typehandler,这个if会进来,重新有做了封装,这步之后 metaObject 就持有了 resultObject
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 自动映射,这里是赋值操作,也就是说此时我们的 resultHandler 其中的值都是 null 或默认值,这里把每个列的值都赋到相应的字段里去了
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 执行完这个方法 resultObject 中的字段才有值,这里传入的其实是 metaObject
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
// 给每一列赋值
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获取字段名称列表
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
// 循环赋值
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
// 从结果集包装类里面获取每个字段的值,涉及到一个很重要的类 TypeHandler
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// 在这里赋值了,其实这里面是给 metaObject 持有的 resultObject中的字段赋值
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERED;
} else {
// 前面的一些可以不用看,这里获取 ResultMapping 原本的 TypeHandler,然后通过 TypeHandler 这个类型处理器去处理值
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}
大致流程如下:
storeObject 方法就是将获取到的 POJO 对象加入到 resultHandler 中的列表字段,最终加入到 multipleResults。
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
// 在这里处理
callResultHandler(resultHandler, resultContext, rowValue);
}
}
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
// resultContext 封装了一些上下文信息 这里包括封装好的 Object,调用 ResultHandler 对象的 handleResult 方法
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
// DefaultResultHandler 列表字段进行添加对象
public void handleResult(ResultContext<? extends Object> context) {
// 就是将获取到的对象加入到 List
list.add(context.getResultObject());
}
流程很简单,就是将之前生成的 POJO 对象添加到 ResultHandler 中的 list 字段中。
执行流程如下:
使用一级缓存,需要在MyBatis配置文件中配置以下语句。该语句共有两个选项,SESSION 或者STATEMENT,默认是 SESSION 级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。一种是 STATEMENT 级别,缓存只对当前执行的这一个 Statement 有效。
<setting name="localCacheScope" value="SESSION"/>
一级缓存的特点:
二级缓存作用范围为 mapper 级别,即同一个 namespace 下的语句,都可以使用到二级缓存,其中增删改会清除二级缓存和一级缓存。开启二级缓存后,执行查询的流程就是:二级缓存 -> 一级缓存 -> 数据库。使用二级缓存,需要在 mapper 映射文件中配置以下语句:
<cache/>
有其他自定义配置,如:size(最多缓存对象的个数)、readOnly(是否只读,若配置可读写,则需要对应的实体类能够序列化)、blocking(若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存)等。
二级缓存的特点:
由于一级缓存和二级缓存都有所缺陷,建议MyBatis缓存在生产环境中关闭,MyBatis单纯作为一个ORM框架使用更为合适。