执行 SQL
使用 SqlSession 提供的 API 也能执行 SQL 语句, 不过这种方使用的少, 还是习惯使用接口的方式
直接调用 SqlSession 的 getMapper 方法就可以为接口实现代理, 所以方法执行前会被拦截, 交给 MapperProxy 拦截, 它实现了 InterceptorHandler 接口
// 得到 MapperMethodInvoker 对象, 调用其 invoke 方法
// 最后交给 MapperMethod 执行, 查看他的 executor 方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 判断 SQL 语句类型 , DQL, DML
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
// DQL 语句单独处理
case SELECT:
// 方法无返回值且参数类型为 ResultHandler
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
}
// 是否返回一个集合
else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
}
// 是否返回一个 Map
else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
}
else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
}
else {
// 将方法的参数转换成 SQL 命令参数 (参数并没有填入 SQL)
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
SqlSession # selectOne
// 查看 SqlSession 的selectOne 方法, 发现它是调用了 selectList 方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 获取封装此 CRUD 的标签的 MappedStatement 对象 (通过 id 对应)
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}
...
}
// 接着查看 Executor 的 query 方法
// 先是检查是否存在二级缓存, 如果不存在, 直接交给 SimpleExecutor 执行
Executor
四大对象之一
SqlSession 包含了一个 Executor 对象 (由 CacheExecutor 实现, 其实这个类也是一个包装 , 它也包含了一个 Executor (默认由 SimpleExecutor 实现))
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 绑定 SQL, 获取到方法参数的映射关系, 参数的元数据, 放到 BoundSql 对象中
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 二级缓存的 Key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
SimpleExecutor
先是调用父类 BaseExecutor 的 query 方法, 检查一级缓存中是否存在需要的结果, 如不存在, 将 SQL 的结果放入一级缓存中
// 查看 BaseExecutor 的 queryFromDatabase 方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);// 这个常量充当占位符
try {
// 执行 SQL
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;
}
// 调用了 SimpleExecutor 的 doQuery 方法
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();
// 创建 SQL 执行器, 同时创建了 ParameterHandler, ResultSetHandler 对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 从数据源中获取连接, 并发送 SQL 语句, 根据 Statement 的类型进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
// 把参数填充到 SQL 中, 执行 SQL
return handler.query(stmt, resultHandler);
}
finally {
closeStatement(stmt);
}
}
先查看这个 Configuration 的 newStatementHandler 方法
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 同时在里面创建了 ParameterHandler , ResultHandler 对象
// 一个 StatementHandler 对象包含了 ParameterHandler , ResultHandler 对象
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 拦截器, 应用插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// 接着看 RoutingStatementHandler 的构造方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 判断此 MappedStatement 的 statemenType
switch (ms.getStatementType()) {
case STATEMENT:
// 相当于 JDBC 的 Statement
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
// 相当于 JDBC 的 PreparedStatement
// 在 PreparedStatementHandler 的构造器中创建了
// ParameterHandler 和 PreparedStatementHandler 对象
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
// 用于调用存储过程的 Statement
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
可以发现, 这里的 PreparedStatementHandler, ParameterHandler, PreparedStatementHandler 在创建完之后, 都应用到了拦截器, 这里就可以对 mybatis 进行插件开发
接着查看这个 prepareStatement 方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取连接, 生成代理对象, ConnectionLogger (mybatis 最新版本3.5.5)
Connection connection = getConnection(statementLog);
// 发送 SQL 语句, 预处理, 同样为 Statement 生成代理对象 PreparedStatementLogger
stmt = handler.prepare(connection, transaction.getTimeout());
// 交给 ParameterHandler 处理, 设置预处理参数
handler.parameterize(stmt);
return stmt;
}
------------------------
// 接着看 StatementHandler 的 parameterize 方法
// 发现是 ParameterHandler 处理, 为 SQL 设置预编译参数
// 查看 ParameterHandler 的 setParameters 方法
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
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();
// 解析参数值 (需要填入 SQL), JDBC 类型
....
try {
// 交给 TypeHandler 处理, 给 SQL 预处理, 设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
StatementHandler
实现类如下 :
实现类为 RoutingStatementHandler , 类似于 CacheExecutor , 也是一个包装, 里面包含了一个 StatementHandler 对象 (默认由 PreparedStatementHandler 实现)
// 查看 PreparedStatementHandler 的 query 方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 被代理对象拦截, 执行 SQL 语句, 将结果集保存在 PreparedStatement 对象中
ps.execute();
// 结果集映射
return resultSetHandler.handleResultSets(ps);
}
ResultSetHandler 默认由 DefaultSetHandler 实现, 查看他的 handleResultSets 方法
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;
// 先处理 resultSet , 包装一下, 存放了数据库中的所有字段, 字段的 Java 类型, JDBC 类型 (mybatis定义的)
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 从 MappedStatement 对象中获取到 resultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 验证 resultMap 的数量
validateResultMapsCount(rsw, resultMapCount);
// 将 resultMap 的结果与 POJO 的属性映射
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理 ResultSet, 放入 multipleResults 集合中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
.....
return collapseSingleResultList(multipleResults);
}
查看这个 handleResultSet 方法
// 一路调用, 来到 DefaultResultSetHandler 的 handleRowValuesForSimpleResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 封装成 POJO, 匹配的构造器, 否者使用 setter 方法为属性填充属性
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 将封装好的 POJO 存放到 ResultHandler 对象中的 List 集合中
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
最后处理这个 multipleResults 集合, 将结果返回出去, 先将其放入一级缓存, 如果存在二级缓存就会放入二级缓存
至于 SQL 的执行细节, 下篇继续