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

MyBatis源码解析:从 Mapper 接口到 SQL 执行的完整链路

MyBatis源码解析:从 Mapper 接口到 SQL 执行的完整链路

  • 一、Mapper 代理对象的创建:sqlSession.getMapper(UserMapper.class)
  • 二、接口方法的执行:mapper.selectUser("coderzpw", 18)
    • 2.1 四大核心组件解析
      • 2.1.1 Executor(执行器):流程控制核心
      • 2.1.2 StatementHandler(语句处理器):JDBC 操作封装
      • 2.1.3 ParameterHandler(参数处理器):参数解析与绑定
      • 2.1.4 ResultSetHandler(结果集处理器):对象映射引擎
    • 2.2 源码解析-完整调用链路:从 Mapper 方法到结果集的全流程
      • 2.2.1 调用链路步骤分解
      • 2.2.2 关键源码串联(MyBatis 3.4.6 版本)
        • 步骤 1:Mapper 代理对象处理方法调用
        • 步骤 2:SqlSession 委托 Executor 执行查询
        • 步骤 3:Executor 执行查询(以 SimpleExecutor 为例)
        • 步骤 4:创建 StatementHandler(RoutingStatementHandler)
        • 步骤 5:StatementHandler 准备 Statement
        • 步骤 6:ParameterHandler 设置参数
        • 步骤 7:StatementHandler 执行查询
        • 步骤 8:ResultSetHandler 处理结果集

MyBatis 的底层本质是对 JDBC 的封装,因此建议忘记 JDBC 执行流程的同学回顾相关知识。可参考这篇文章:【JDBC 核心执行流程详解】

MyBatis 中,常用的查询方式是通过 Mapper 接口代理实现,而非直接调用 sqlSession.selectList 例如:

// Mapper 接口定义
public interface UserMapper {List<User> selectUser(@Param("name") String name, @Param("age") Integer age);
}// 应用层调用(通过代理对象执行)
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser("coderzpw", 18); // 核心:代理对象处理方法调用

接下来文章内容主要围绕上述代码的底层源码逻辑展开讲解

一、Mapper 代理对象的创建:sqlSession.getMapper(UserMapper.class)

当调用 sqlSession.getMapper(UserMapper.class) 时,MyBatis 通过 MapperProxyFactory 创建动态代理对象,涉及到源码如下:

// DefaultSqlSession.java
public <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);
}
// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 从已知的Mapper映射中查找对应的Mapper代理工厂final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);// 如果找不到对应的Mapper代理工厂,抛出绑定异常if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 使用代理工厂创建Mapper代理实例return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}
}
// MapperProxyFactory.javapublic T newInstance(SqlSession sqlSession) {// 创建Mapper代理处理器,它实现了InvocationHandler接口// 参数1:当前SqlSession,用于执行SQL语句// 参数2:Mapper接口类型// 参数3:方法缓存,用于缓存Mapper方法对应的SQL语句final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);
}protected T newInstance(MapperProxy<T> mapperProxy) {// 使用JDK动态代理创建代理对象// 参数1:类加载器,使用Mapper接口的类加载器// 参数2:代理对象要实现的接口,这里就是Mapper接口// 参数3:代理对象的调用处理器,负责处理代理对象方法的调用return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

梳理源码逻辑后,执行到 UserMapper mapper = sqlSession.getMapper(UserMapper.class) 时,MyBatis 通过动态代理为 UserMapper 接口生成代理对象,该对象会在后续数据访问中把接口方法调用转为 SQL 执行流程。

二、接口方法的执行:mapper.selectUser(“coderzpw”, 18)

2.1 四大核心组件解析

后续查询由 MyBatis 四大核心组件协同完成:

  • Executor(执行器):控制整体查询流程
  • StatementHandler(语句处理器):准备并执行 SQL
  • ParameterHandler(参数处理器):处理 SQL 参数设置
  • ResultSetHandler(结果集处理器):将查询结果映射为 Java 对象

2.1.1 Executor(执行器):流程控制核心

类层级:

Executor(接口)
├─ BaseExecutor(抽象类,实现一级缓存 + 事务管理)
│  ├─ SimpleExecutor(默认执行器,每次创建新 Statement)
│  ├─ ReuseExecutor(复用 Statement,基于 SimpleExecutor 扩展)
│  └─ BatchExecutor(批量执行,基于 SimpleExecutor 扩展)
└─ CachingExecutor(二级缓存装饰器,包装 Executor 实现缓存)

关键方法:query(处理查询)、update(处理更新)

核心源码片段(BaseExecutor):

public abstract class BaseExecutor implements Executor {// 一级缓存(本地缓存)private final PerpetualCache localCache = new PerpetualCache("LocalCache");// 占位符对象,用于标记正在执行的查询private static final Object EXECUTION_PLACEHOLDER = new Object();@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {BoundSql boundSql = ms.getBoundSql(parameter);// 生成缓存键(基于 SQL、参数、RowBounds 等)CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// ...List<E> list;try {queryStack++;// 从本地缓存获取结果list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;// 如果缓存命中,处理本地缓存的输出参数(如存储过程的OUT参数)if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// 缓存未命中,从数据库查询并缓存结果list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}// ...// 返回查询结果列表return list;}private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {List<E> list;// 先在缓存中放入占位符,标记该查询正在执行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;}// 子类实现具体执行逻辑(如 SimpleExecutor 的 doQuery)protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
}

2.1.2 StatementHandler(语句处理器):JDBC 操作封装

类层级:

StatementHandler(接口)
├─ BaseStatementHandler(抽象类,实现公共逻辑)
│  ├─ SimpleStatementHandler(处理普通 Statement,无参数)
│  ├─ PreparedStatementHandler(处理预编译 PreparedStatement,带参数)
│  └─ CallableStatementHandler(处理存储过程 CallableStatement)
└─ RoutingStatementHandler(路由类,不直接处理 SQL,仅根据 StatementType 选择具体实现)

关键方法:prepare(创建 Statement)、parameterize(设置参数)、query(执行查询)

核心源码片段(BaseExecutor):

public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, ms, parameter, rowBounds, resultHandler, boundSql);}// 创建预编译 Statement@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();// 处理需要返回自动生成键的情况(如INSERT语句)if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {// 如果没有指定主键列名,使用默认方式返回所有生成的键return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {// 如果指定了主键列名,只返回这些列的生成键return connection.prepareStatement(sql, keyColumnNames);}} // 处理需要指定结果集类型的情况else if (mappedStatement.getResultSetType() != null) {return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY  // 设置结果集为只读);} // 默认情况,使用标准的PreparedStatementelse {return connection.prepareStatement(sql);}}// 执行查询并处理结果集@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 执行 SQLps.execute(); // 委托 ResultSetHandler 处理结果集return resultSetHandler.handleResultSets(ps);}
}

2.1.3 ParameterHandler(参数处理器):参数解析与绑定

实现类:DefaultParameterHandler
关键逻辑:将方法参数(如 nameage)映射到 SQL 占位符(?),通过 TypeHandler 转换类型
核心源码片段

public void setParameters(PreparedStatement ps) {// 设置错误上下文,记录当前活动为"设置参数",并关联到对应的参数映射IDErrorContext.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);// 仅处理输入参数(IN或INOUT),忽略输出参数(OUT)if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();// 1. 首先检查是否为额外参数(如动态SQL中定义的参数)if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} // 2. 检查参数对象是否为nullelse if (parameterObject == null) {value = null;} // 3. 检查参数对象是否可以直接使用类型处理器处理(如基本类型)else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} // 4. 对于复杂对象,使用MetaObject反射获取属性值else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 获取参数的类型处理器和JDBC类型TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();// 处理null值的JDBC类型if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// 使用类型处理器将Java对象转换为JDBC参数并设置到PreparedStatement中// 注意:JDBC参数索引从1开始,而不是从0开始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);}}}}
}

2.1.4 ResultSetHandler(结果集处理器):对象映射引擎

实现类:DefaultResultSetHandler
关键逻辑:将 ResultSet 逐行映射为 Java 对象,支持 ResultMap 配置的字段到属性映射
核心源码片段:

public List<Object> handleResultSets(Statement stmt) throws SQLException {// 存储所有结果List<Object> results = new ArrayList<>();// 获取第一个结果集ResultSetWrapper rsw = getFirstResultSet(stmt);List<ResultMap> resultMaps = mappedStatement.getResultMaps();// 处理主结果集for (ResultMap resultMap : resultMaps) {if (rsw == null) break;handleResultSet(rsw, resultMap, results, null);rsw = getNextResultSet(stmt);// ...清理资源}// 处理命名结果集(如存储过程返回的多个结果集)String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {for (String resultSet : resultSets) {if (rsw == null) break;ResultMapping mapping = nextResultMaps.get(resultSet);if (mapping != null) {ResultMap nestedMap = configuration.getResultMap(mapping.getNestedResultMapId());handleResultSet(rsw, nestedMap, null, mapping);}rsw = getNextResultSet(stmt);// ...清理资源}}// 整理结果return collapseSingleResultList(results);
}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) {DefaultResultHandler defaultHandler = new DefaultResultHandler(objectFactory);handleRowValues(rsw, resultMap, defaultHandler, rowBounds, null);multipleResults.add(defaultHandler.getResultList());} // 使用自定义结果处理器else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// 关闭结果集closeResultSet(rsw.getResultSet());}
}

2.2 源码解析-完整调用链路:从 Mapper 方法到结果集的全流程

接下来聚焦 List<User> users = mapper.selectUser("coderzpw", 18) 调用,执行时通过代理对象触发 MapperProxyinvoke 方法,进而调用 MapperMethodexecute 方法,MyBatis 会依方法签名和参数确定并转换 SQL

2.2.1 调用链路步骤分解

Mapper接口方法调用(如UserMapper.selectUser(name, age))
├─ 【MapperProxy(代理对象)】
│   ├─ 拦截接口方法调用,触发 invoke() 方法
│   ├─ 从缓存中获取对应的 MapperMethod 对象
│   └─ 调用 mapperMethod.execute(sqlSession, args)
│
├─ 【MapperMethod】
│   ├─ 根据方法类型(SELECT/INSERT/UPDATE/DELETE)选择执行策略
│   ├─ 解析方法参数,构建参数对象
│   └─ 调用 sqlSession 对应方法(如 selectList/selectOne/insert 等)
│
├─ 【DefaultSqlSession】
│   └─ 委托 Executor.query() 执行查询(Executor 组件)
│
├─ 【ExecutorSimpleExecutor)】
│   ├─ 创建 StatementHandlerPreparedStatementHandler)
│   ├─ 调用 handler.prepare() 创建 PreparedStatement(含预编译 SQL)
│   ├─ 调用 handler.parameterize() 触发 ParameterHandler 设置参数
│   └─ 调用 handler.query() 执行 SQL,获取 ResultSet
│
├─ 【StatementHandlerPreparedStatementHandler)】
│   ├─ instantiateStatement() 创建 PreparedStatementSQL: "select * from user where name = ? and age > ?")
│   ├─ setParameters() 委托 ParameterHandler 绑定参数(name=? 对应 "张三",age=? 对应 20)
│   └─ execute() 执行查询,返回 ResultSetResultSetHandler
│
├─ 【ParameterHandlerDefaultParameterHandler)】
│   └─ setParameters() 遍历参数映射,通过 TypeHandler 转换并设置到 PreparedStatement
│
└─ 【ResultSetHandlerDefaultResultSetHandler)】├─ handleResultSets() 遍历 ResultSet 行├─ createResultObject() 创建 User 实例└─ 通过反射将列值(name、age 等)设置到 User 对象属性

2.2.2 关键源码串联(MyBatis 3.4.6 版本)

步骤 1:Mapper 代理对象处理方法调用
// MapperProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 处理 Object 类的通用方法(如 toString、equals 等)if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} // 处理 Java 8 引入的接口默认方法else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 对 Mapper 接口定义的方法进行缓存和执行final MapperMethod mapperMethod = cachedMapperMethod(method);// 执行实际的数据库操作return mapperMethod.execute(sqlSession, args);
}
// MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {Object result;// 根据 SQL 命令类型选择执行方法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;}case SELECT:// 处理返回值为 void 且有 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);}// 处理返回值为 Cursor 的情况else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);}// 处理返回单个对象的情况else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);// 处理返回值为 Optional 的情况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());}// 处理返回值为 null 但不允许 null 的情况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;
}// 处理多参数情况,将参数转换为 SQL 参数
public Object convertArgsToSqlCommandParam(Object[] args) {// 无参数if (args == null || args.length == 0) {return null;}// 单个参数且无 @Param 注解else if (args.length == 1 && !hasNamedParameters) {return args[0];}// 多个参数或有 @Param 注解else {final Map<String, Object> param = new ParamMap<>();int i = 0;// 处理 @Param 注解的参数for (String name : paramNames) {param.put(name, args[i++]);}// 为参数添加 param1, param2 等键if (paramNames.size() < args.length) {for (int j = paramNames.size(); j < args.length; j++) {param.put("param" + String.valueOf(j + 1), args[j]);}}return param;}
}
步骤 2:SqlSession 委托 Executor 执行查询
// DefaultSqlSession.java
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {// 根据 statement ID 获取 MappedStatementMappedStatement ms = configuration.getMappedStatement(statement);// 委托 Executor 执行查询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();}
}// 处理集合参数
private Object wrapCollection(final Object object) {if (object instanceof Collection) {StrictMap<Object> map = new StrictMap<>();map.put("collection", object);if (object instanceof List) {map.put("list", object);}return map;} else if (object != null && object.getClass().isArray()) {StrictMap<Object> map = new StrictMap<>();map.put("array", object);return map;}return object;
}
步骤 3:Executor 执行查询(以 SimpleExecutor 为例)
// BaseExecutor.java
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 获取 BoundSql(包含解析后的 SQL 和参数映射信息)BoundSql boundSql = ms.getBoundSql(parameter);// 创建缓存键(基于 SQL、参数、RowBounds 等)CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);// 执行查询(可能从缓存获取)return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}@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.");}// 先清空本地缓存(针对 select 语句,一级缓存会在查询前清空)if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;// 从本地缓存获取结果list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {// 处理存储过程的输出参数handleLocallyCachedOutputParameters(ms, key, parameter);} else {// 本地缓存未命中,执行数据库查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {// 延迟加载队列处理for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// 清空延迟加载队列deferredLoads.clear();// 一级缓存的作用域是 session,默认情况下,select 语句执行后会清空本地缓存if (configuration.getLocalCacheScope() == LocalCacheScope.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;// 先在缓存中放入占位符,避免递归查询localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 调用子类的 doQuery 方法执行实际查询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.java
@Override
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 实现)StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 准备 Statementstmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 关闭 StatementcloseStatement(stmt);}
}// 准备 Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;// 获取数据库连接Connection connection = getConnection(statementLog);// 准备 Statementstmt = handler.prepare(connection, transaction.getTimeout());// 设置参数handler.parameterize(stmt);return stmt;
}
步骤 4:创建 StatementHandler(RoutingStatementHandler)
// Configuration.java
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// 创建 StatementHandler(实际创建的是 RoutingStatementHandler)StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 应用插件(如果有)statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;
}// RoutingStatementHandler.java
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// 根据 StatementType 选择实际的 StatementHandler 实现switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:// 对于预编译语句,使用 PreparedStatementHandlerdelegate = 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());}
}
步骤 5:StatementHandler 准备 Statement
// BaseStatementHandler.java
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 实例化 Statementstatement = instantiateStatement(connection);// 设置超时时间setStatementTimeout(statement, transactionTimeout);// 设置 fetchSizesetFetchSize(statement);return statement;} catch (SQLException e) {closeStatement(statement);throw e;} catch (Exception e) {closeStatement(statement);throw new ExecutorException("Error preparing statement.  Cause: " + e, e);}
}// PreparedStatementHandler.java
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();// 获取主键生成策略if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {// 没有指定主键列,使用 JDBC 3.0 规范的方法获取自增主键return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);} else {// 指定了主键列,使用指定的列获取自增主键return connection.prepareStatement(sql, keyColumnNames);}} else if (mappedStatement.getResultSetType() != null) {// 设置结果集类型return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {// 默认情况,创建预编译语句return connection.prepareStatement(sql);}
}
步骤 6:ParameterHandler 设置参数
// BaseStatementHandler.java
@Override
public void parameterize(Statement statement) throws SQLException {// 委托 ParameterHandler 设置参数parameterHandler.setParameters((PreparedStatement) statement);
}// DefaultParameterHandler.java
@Override
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();// 处理动态参数(如 _parameter、_databaseId)if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);}// 处理参数为 null 的情况else if (parameterObject == null) {value = null;}// 处理参数为基本类型的情况else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;}// 处理参数为对象或 Map 的情况else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 获取类型处理器TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();// 处理 jdbcType 为 null 的情况if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// 设置参数(核心:类型处理器将 Java 对象转换为 JDBC 类型)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);}}}}
}
步骤 7:StatementHandler 执行查询
// PreparedStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {// 转换为 PreparedStatementPreparedStatement ps = (PreparedStatement) statement;// 执行 SQLps.execute();// 委托 ResultSetHandler 处理结果集return resultSetHandler.handleResultSets(ps);
}
步骤 8:ResultSetHandler 处理结果集
// DefaultResultSetHandler.java
@Override
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 rs = getFirstResultSet(stmt);// 获取结果映射列表List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();// 验证结果映射数量validateResultMapsCount(rs, resultMapCount);// 处理所有结果集while (rs != null && resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get(resultSetCount);// 处理单个结果集handleResultSet(rs, resultMap, multipleResults, null);// 获取下一个结果集(适用于存储过程返回多个结果集的情况)rs = getNextResultSet(stmt);// 清理资源cleanUpAfterHandlingResultSet();resultSetCount++;}// 处理结果集映射String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {while (rs != null && resultSetCount < resultSets.length) {// 处理命名结果集ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) {String nestedResultMapId = parentMapping.getNestedResultMapId();ResultMap resultMap = configuration.getResultMap(nestedResultMapId);handleResultSet(rs, resultMap, null, parentMapping);}rs = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}}// 处理单个结果集的情况,将其展开为列表return collapseSingleResultList(multipleResults);
}// 处理单个结果集
private void handleResultSet(ResultSet rs, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {// 处理嵌套结果集handleRowValues(rs, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {// 处理普通结果集if (resultHandler == null) {// 创建默认结果处理器DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);// 处理行值handleRowValues(rs, resultMap, defaultResultHandler, rowBounds, null);// 将结果添加到多个结果列表中multipleResults.add(defaultResultHandler.getResultList());} else {// 使用用户提供的结果处理器handleRowValues(rs, resultMap, resultHandler, rowBounds, null);}}} finally {// 关闭结果集closeResultSet(rs);}
}// 处理行值
private void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {// 处理包含嵌套结果映射的情况ensureNoRowBounds();checkResultHandler();handleRowValuesForNestedResultMap(rs, resultMap, resultHandler, rowBounds, parentMapping);} else {// 处理简单结果映射的情况handleRowValuesForSimpleResultMap(rs, resultMap, resultHandler, rowBounds);}
}// 处理简单结果映射的行值
private void handleRowValuesForSimpleResultMap(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds) throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<>();// 跳过分页偏移量skipRows(rs, rowBounds.getOffset());// 处理行数据while (shouldProcessMoreRows(resultContext, rowBounds) && rs.next()) {// 获取判别式结果映射(用于动态结果映射)ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);// 获取行值(创建结果对象并填充数据)Object rowValue = getRowValue(rs, discriminatedResultMap);// 将行值添加到结果上下文中resultContext.nextResultObject(rowValue);// 如果有结果处理器,调用它处理结果if (resultHandler != null) {resultHandler.handleResult(resultContext);}}
}// 获取行值(创建结果对象并填充数据)
private Object getRowValue(ResultSet rs, ResultMap resultMap) throws SQLException {final ResultLoaderMap lazyLoader = new ResultLoaderMap();// 创建结果对象Object rowValue = createResultObject(rs, resultMap, lazyLoader, null);if (rowValue != null && !resultMap.isPrimitive()) {// 创建元对象MetaObject metaObject = configuration.newMetaObject(rowValue);// 标记是否找到值boolean foundValues = this.useConstructorMappings;// 处理自动映射if (shouldApplyAutomaticMappings(resultMap, false)) {foundValues = applyAutomaticMappings(rs, resultMap, metaObject, null) || foundValues;}// 处理手动映射foundValues = applyPropertyMappings(rs, resultMap, metaObject, lazyLoader, null) || foundValues;// 如果没有找到任何值且需要非空结果,则返回 nullfoundValues = lazyLoader.size() > 0 || foundValues;rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}return rowValue;
}// 创建结果对象
private Object createResultObject(ResultSet rs, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {this.useConstructorMappings = false; // 重置构造函数映射标志// 获取结果类型final Class<?> resultType = resultMap.getType();// 创建元对象处理器final List<Class<?>> constructorArgTypes = new ArrayList<>();final List<Object> constructorArgs = new ArrayList<>();// 创建结果对象Object resultObject = createResultObject(rs, resultType, constructorArgTypes, constructorArgs, columnPrefix);// 如果结果类型不是基本类型if (resultObject != null && !resultType.isInterface()) {// 检查是否有构造函数映射if (shouldApplyAutomaticMappings(resultMap, true)) {// 应用自动映射applyAutomaticMappings(rs, resultMap, configuration.newMetaObject(resultObject), columnPrefix);}// 应用属性映射applyPropertyMappings(rs, resultMap, configuration.newMetaObject(resultObject), lazyLoader, columnPrefix);}// 标记使用了构造函数映射this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();return resultObject;
}

相关文章:

MyBatis源码解析:从 Mapper 接口到 SQL 执行的完整链路

MyBatis源码解析&#xff1a;从 Mapper 接口到 SQL 执行的完整链路 一、Mapper 代理对象的创建&#xff1a;sqlSession.getMapper(UserMapper.class)二、接口方法的执行&#xff1a;mapper.selectUser("coderzpw", 18)2.1 四大核心组件解析2.1.1 Executor&#xff08…...

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— FormWave组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 构建一个美观、动态的登录表单&#xff0…...

双目相机深度的误差分析(基线长度和相机焦距的选择)

全文基于针孔模型和基线水平放置来讨论 影响双目计算深度的因素&#xff1a; 1、基线长度&#xff1a;两台相机光心之间距离2、相机焦距&#xff08;像素&#xff09;&#xff1a; f x f_x fx​&#xff08;或 f y f_y fy​&#xff09;为焦距 f f f和一个缩放比例的乘积。在…...

Pytorch Geometric官方例程pytorch_geometric/examples/link_pred.py环境安装教程及图数据集制作

最近需要训练图卷积神经网络&#xff08;Graph Convolution Neural Network, GCNN&#xff09;&#xff0c;在配置GCNN环境上总结了一些经验。 我觉得对于初学者而言&#xff0c;图神经网络的训练会有2个难点&#xff1a; ①环境配置 ②数据集制作 一、环境配置 我最初光想…...

React---day6、7

6、组件之间进行数据传递 **6.1 父传子&#xff1a;**props传递属性 父组件&#xff1a; <div><ChildCpn name"蒋乙菥" age"18" height"1,88" /> </div>子组件&#xff1a; export class ChildCpn extends React.Component…...

hook组件-useEffect、useRef

hook组件-useEffect、useRef useEffect 用法及执行机制 WillMount -> render -> DidMount ShouldUpdate -> WillUpdate -> render -> DidUpdate WillUnmount(只有这个安全) WillReceiveProps useEffect(callback) 默认所有依赖都更新useEffect(callback, [])&am…...

功能结构整理

C# Sxer Sxer.Base&#xff1a;基础子功能 Sxer.Base.Debug&#xff1a;打印 Sxer.Utility&#xff1a;工具类 Sxer.CustomFunction&#xff1a;独立功能点开发 Unity...

企业级开发中的 maven-mvnd 应用实践

1. 引言:Maven 在企业级开发中的挑战 1.1 Maven 构建的常见痛点 在大型 Java 项目中,Maven 是主流的构建工具,但随着项目的复杂度增加,其性能瓶颈逐渐显现: 构建速度慢:每次执行 mvn clean install 都需要重新加载插件和依赖。重复构建浪费资源:即使未修改源码,仍会触…...

yolov12毕设前置知识准备 1

1 什么是目标检测呢&#xff1f; 目标检测&#xff08;Object Detection&#xff09;主要用于识别图像或视频中特定类型物体的位置&#xff0c;并标注其类别。 简单来说&#xff0c;就是让计算机像人类一样 “看懂” 图像内容&#xff0c;不仅能识别出物体&#xff08;如人、…...

随机游动算法解决kSAT问题

input&#xff1a;n个变量的k-CNF公式 ouput&#xff1a;该公式的一组满足赋值或宣布没有满足赋值 算法步骤&#xff1a; 随机均匀地初始化赋值 a ∈ { 0 , 1 } n a\in\{0,1\}^n a∈{0,1}n.重复t次&#xff08;后面会估计这个t&#xff09;&#xff1a; a. 如果在当前赋值下…...

《Discuz! X3.5开发从入门到生态共建》第1章 Discuz! 的前世今生-优雅草卓伊凡

《Discuz! X3.5开发从入门到生态共建》第1章 Discuz! 的前世今生-优雅草卓伊凡 第一节 从康盛创想到腾讯收购&#xff1a;PC时代的辉煌 1.1 Discuz! 的诞生&#xff1a;康盛创想的开源梦想 2001年&#xff0c;中国互联网正处于萌芽阶段&#xff0c;个人网站和论坛开始兴起。…...

azure web app创建分步指南系列之一

什么是 Azure Web 应用? Azure Web 应用是 Azure 应用服务的一部分,是一个完全托管的平台,用于开发、部署和扩展 Web 应用程序。它支持各种编程语言和框架,例如 .NET、Java、Python、PHP 和 Node.js,使开发人员能够构建强大的 Web 应用程序,而无需担心底层基础架构。借助…...

PyTorch实战——基于生成对抗网络生成服饰图像

PyTorch实战——基于生成对抗网络生成服饰图像 0. 前言1. 模型分析与数据准备2. 判别器3. 生成器4. 模型训练5. 模型保存与加载相关链接0. 前言 我们已经学习了生成对抗网络 (Generative Adversarial Network, GAN) 的工作原理,接下来,将学习如何将其应用于生成其他形式的内…...

笔试强训:Day6

一、小红的口罩&#xff08;贪心优先级队列&#xff09; 登录—专业IT笔试面试备考平台_牛客网 #include<iostream> #include<queue> #include<vector> using namespace std; int n,k; int main(){//用一个小根堆 每次使用不舒适度最小的cin>>n>&…...

【Hexo】4.Hexo 博客文章进行加密

安装 npm install --save hexo-blog-encrypt1-快速使用 将“ password”添加到您的文章信息头就像这样&#xff1a; password: 123456 ---2-按标签加密 1.修改文章信息头如下&#xff1a; title: Hello World tags: - 加密文章tag date: 2020-03-13 21:12:21 password: muyiio…...

Android --- ObjectAnimator 和 TranslateAnimation有什么区别

文章目录 2. 作用范围和功能2. 动画表现3. 是否修改 View 的属性4. 适用场景5. 性能总结&#xff1a; ObjectAnimator 和 TranslateAnimation 都是 Android 中常用的动画类型&#xff0c;但它们有以下几个关键的区别&#xff1a; 2. 作用范围和功能 ObjectAnimator&#xff1a…...

小白的进阶之路系列之四----人工智能从初步到精通pytorch自定义数据集下

本篇涵盖的内容 在之前的文章中,我们已经讨论了如何获取数据,转换数据以及如何准备自定义数据集,本篇文章将涵盖更加深入的问题,希望通过详细的代码示例,帮助大家了解PyTorch自定义数据集是如何应对各种复杂实际情况中,数据处理的。 更加详细的,我们将讨论下面一些内容…...

安卓添加设备节点权限和selinux访问权限

# 1 修改设备节点权限及配置属性设置节点值 ## 1.1 修改设备节点权限 ### 1.1.1 不会手动卸载的节点 在system/core/rootdir/init.rc中添加节点权限 在on boot下面添加 chown system system /sys/kernel/usb/host chmod 0664 /sys/kernel/usb/host ### 1.1.2 支持热插拔的…...

谷歌Stitch:AI赋能UI设计,免费高效新利器

在AI技术日新月异的今天&#xff0c;各大科技巨头都在不断刷新我们对智能工具的认知。最近&#xff0c;谷歌在其年度I/O开发者大会期间&#xff0c;除了那些聚光灯下的重磅发布&#xff0c;还悄然上线了一款令人惊喜的AI工具——Stitch。这是一款全新的、完全免费的AI驱动UI&am…...

运营商地址和ip属地一样吗?怎么样更改ip属地地址

‌在互联网时代&#xff0c;IP属地和运营商地址是两个经常被提及的概念&#xff0c;但它们是否相同&#xff1f;如何更改IP属地地址&#xff1f;这些问题困扰着许多网民。本文将深入探讨这两个概念的区别&#xff0c;并详细介绍更改IP属地地址的方法。 一、运营商地址和IP属地一…...

在QT中,利用charts库绘制FFT图形

第1章 添加charts库 1.1 .pro工程添加chart库 1.1.1 在.pro工程里面添加charts库 1.1.2 在需要使用的地方添加这两个库函数&#xff0c;顺序一点不要搞错&#xff0c;先添加.pro&#xff0c;否则编译器会找不到这两个.h文件。 第2章 Charts关键绘图函数 2.1 QChart 类 QChart 是…...

ChatGPT + 知网 + 知乎,如何高效整合信息写出一篇专业内容?

——写作&#xff0c;不是闭门造车&#xff0c;而是高效聚合 &#x1f9e0; 为什么“信息整合力”才是AI时代的核心写作能力&#xff1f; 现在的写作&#xff0c;不缺工具&#xff0c;也不缺资料&#xff0c;缺的是&#xff1a; 把 scattered info 变成 structured idea 的能力…...

流媒体协议分析:流媒体传输的基石

在流媒体传输过程中&#xff0c;协议的选择至关重要&#xff0c;它决定了数据如何封装、传输和解析&#xff0c;直接影响着视频的播放质量和用户体验。本文将深入分析几种常见的流媒体传输协议&#xff0c;探讨它们的特点、应用场景及优缺点。 协议分类概述 流媒体传输协议根据…...

vscode中让文件夹一直保持展开不折叠

vscode中让文件夹一直保持展开不折叠 问题 很多小伙伴使用vscode发现空文件夹会折叠显示, 让人看起来非常难受, 如下图 解决办法 首先打开设置->setting, 搜索compact Folders, 去掉勾选即可, 如下图所示 效果如下 看起来非常爽 ! ! !...

JAVA-springboot整合Mybatis

SpringBoot从入门到精通-第15章 MyBatis框架 学习MyBatis心路历程 2022年学习java基础时候&#xff0c;想着怎么使用java代码操作数据库&#xff0c;咨询了项目上开发W同事&#xff0c;没有引用框架&#xff0c;操作数据库很麻烦&#xff0c;就帮我写好多行代码&#xff0c;就…...

深度学习pycharm debug

深度学习中&#xff0c;Debug 是定位并解决代码逻辑错误&#xff08;如张量维度不匹配&#xff09;、训练异常&#xff08;如 Loss 波动&#xff09;、数据问题&#xff08;如标签错误&#xff09;的关键手段&#xff0c;通过打印维度、可视化梯度等方法确保模型正常运行、优化…...

MicroPython+L298N+ESP32控制电机转速

要使用MicroPython控制L298N电机驱动板来控制电机的转速&#xff0c;你可以通过PWM&#xff08;脉冲宽度调制&#xff09;信号来调节电机速度。L298N是一个双H桥驱动器&#xff0c;可以同时控制两个电机的正反转和速度。 硬件准备&#xff1a; 1. L298N 电机控制板 2. ESP32…...

Hive的存储格式如何优化?

Hive的存储格式对查询性能、存储成本和数据处理效率有显著影响。以下是主流存储格式的特点、选择标准和优化方法&#xff1a; 一、主流存储格式对比 特性ORC&#xff08;Optimized Row Columnar&#xff09;ParquetTextFile&#xff08;默认&#xff09;SequenceFile数据布局…...

在部署了一台mysql5.7的机器上部署mysql8.0.35

在已部署 MySQL 5.7 的机器上部署 MySQL 8.0.35 的完整指南 在同一台服务器上部署多个 MySQL 版本需要谨慎规划&#xff0c;避免端口冲突和数据混淆。以下是详细的部署步骤&#xff1a; 一、规划配置 端口分配 MySQL 5.7&#xff1a;使用默认端口 3306MySQL 8.0.35&#xff1…...

OpenCV CUDA模块结构分析与形状描述符------在 GPU 上计算图像的原始矩(spatial moments)函数spatialMoments()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于在 GPU 上计算图像的原始矩&#xff08;spatial moments&#xff09;。这些矩可用于描述图像中物体的形状特征&#xff0c;如面积、质…...