MyBatis StatementHandler 深度解析:数据库交互的核心执行者 StatementHandler 是 MyBatis 中直接与数据库交互的核心接口 ,负责管理 JDBC 的 Statement 对象(Statement/PreparedStatement/CallableStatement),协调 ParameterHandler(参数设置)和 ResultSetHandler(结果映射),完成 “SQL 预编译→参数绑定→SQL 执行→结果处理” 的全流程。从 “接口定位→架构设计→核心实现→调用链路” 四个维度,彻底拆解 StatementHandler 的工作机制,并揭示其背后的设计模式。
StatementHandler 核心定位与接口规范 核心职责 StatementHandler 是 MyBatis 与数据库交互的 “最后一公里”,核心职责可概括为 5 点:
创建 Statement 对象 :根据 SQL 类型(无参数 / 预编译 / 存储过程)创建对应的 JDBC Statement;
SQL 预编译 :对预编译 SQL(PreparedStatement)进行编译,提升执行效率;
参数绑定 :委托 ParameterHandler 为 SQL 占位符(?)设置参数;
SQL 执行 :执行 select/insert/update/delete 及批量操作;
结果转发 :将执行结果(ResultSet)委托 ResultSetHandler 映射为 Java 对象。
接口定义:核心方法规范 StatementHandler 接口定义了数据库交互的标准方法,所有实现类需遵循该规范:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface StatementHandler { Statement prepare (Connection connection, Integer transactionTimeout) throws SQLException ; void parameterize (Statement statement) throws SQLException ; void batch (Statement statement) throws SQLException ; int update (Statement statement) throws SQLException ; <E> List<E> query (Statement statement, ResultHandler resultHandler) throws SQLException ; <E> Cursor<E> queryCursor (Statement statement) throws SQLException ; BoundSql getBoundSql () ; ParameterHandler getParameterHandler () ; }
架构设计:接口 + 抽象基类 + 具体实现(模板方法 + 策略模式) MyBatis 对 StatementHandler 的实现采用 “接口定义规范 + 抽象基类封装通用逻辑 + 具体类实现差异化功能” 的架构,并通过 模板方法模式 (抽象基类)和 策略模式 (路由类)降低耦合,提升扩展性。
架构层级图
StatementHandler 接口
BaseStatementHandler 抽象基类
SimpleStatementHandler 无参数执行器
PreparedStatementHandler 预编译执行器
根据 statementType 选择 delegate
RoutingStatementHandler 路由执行器 策略模式
StatementHandler 接口BaseStatementHandler 抽象基类SimpleStatementHandler 无参数执行器PreparedStatementHandler 预编译执行器根据 statementType 选择 delegateRoutingStatementHandler 路由执行器(策略模式)
设计模式解析:
模板方法模式 :BaseStatementHandler 实现 prepare() 方法的通用框架(如设置超时、fetchSize),将差异化逻辑(创建 Statement)抽象为 instantiateStatement() 由子类实现;
策略模式 :RoutingStatementHandler 作为 “策略路由”,根据 MappedStatement 的 statementType 选择对应的 delegate(Simple/Prepared/Callable),上层无需关心具体实现。
核心实现类解析 1. BaseStatementHandler:抽象基类(模板方法的核心) BaseStatementHandler 是所有具体执行器的父类,封装了 通用逻辑 (如参数 / 结果处理器创建、超时设置、Statement 关闭),暴露 instantiateStatement() 抽象方法由子类实现差异化的 Statement 创建逻辑。
核心源码与逻辑拆解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 public abstract class BaseStatementHandler implements StatementHandler { protected final Configuration configuration; protected final ParameterHandler parameterHandler; protected final ResultSetHandler resultSetHandler; protected final Executor executor; protected final MappedStatement mappedStatement; protected final RowBounds rowBounds; protected final BoundSql boundSql; protected BaseStatementHandler (Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this .configuration = ms.getConfiguration(); this .executor = executor; this .mappedStatement = ms; this .rowBounds = rowBounds; this .boundSql = boundSql; this .parameterHandler = configuration.newParameterHandler(ms, parameter, boundSql); this .resultSetHandler = configuration.newResultSetHandler(executor, ms, rowBounds, parameterHandler, resultHandler, boundSql); if (boundSql == null ) { generateKeys(parameter); this .boundSql = ms.getBoundSql(parameter); } } @Override public Statement prepare (Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null ; try { statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } } protected abstract Statement instantiateStatement (Connection connection) throws SQLException ; protected void setStatementTimeout (Statement stmt, Integer transactionTimeout) throws SQLException { Integer queryTimeout = mappedStatement.getTimeout() != null ? mappedStatement.getTimeout() : configuration.getDefaultStatementTimeout(); if (queryTimeout != null ) { stmt.setQueryTimeout(queryTimeout); } StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout); } protected void setFetchSize (Statement stmt) throws SQLException { Integer fetchSize = mappedStatement.getFetchSize() != null ? mappedStatement.getFetchSize() : configuration.getDefaultFetchSize(); if (fetchSize != null ) { stmt.setFetchSize(fetchSize); } } protected void closeStatement (Statement statement) { if (statement != null ) { try { statement.close(); } catch (SQLException e) { } } } protected void generateKeys (Object parameter) { KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processBefore(executor, mappedStatement, null , parameter); } @Override public BoundSql getBoundSql () { return boundSql; } @Override public ParameterHandler getParameterHandler () { return parameterHandler; } }
核心价值:
解耦通用与差异化逻辑 :将超时设置、fetchSize 配置、Statement 关闭等通用逻辑封装,子类仅需关注 “如何创建 Statement”;
依赖注入 :在构造函数中创建 ParameterHandler 和 ResultSetHandler,建立 StatementHandler 与其他组件的协作关系;
主键预处理 :支持 SQL 执行前生成自增主键(如 useGeneratedKeys=true),确保主键能正确设置到实体对象。
2. 具体执行器:差异化实现(对应 JDBC 三种 Statement) MyBatis 提供三个具体执行器,分别对应 JDBC 的三种 Statement 类型,覆盖不同 SQL 场景。
(1)SimpleStatementHandler:无参数 SQL 执行器 对应 JDBC 的 Statement,不支持参数占位符(?) ,适合执行无参数的 SQL(如 SELECT * FROM user),parameterize() 方法为空实现。
核心源码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public class SimpleStatementHandler extends BaseStatementHandler { public SimpleStatementHandler (Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super (executor, ms, parameter, rowBounds, resultHandler, boundSql); } @Override public int update (Statement statement) throws SQLException { String sql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; if (keyGenerator instanceof Jdbc3KeyGenerator) { statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { statement.execute(sql); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { statement.execute(sql); rows = statement.getUpdateCount(); } return rows; } @Override public <E> List<E> query (Statement statement, ResultHandler resultHandler) throws SQLException { String sql = boundSql.getSql(); statement.execute(sql); return resultSetHandler.handleResultSets(statement); } @Override public void batch (Statement statement) throws SQLException { String sql = boundSql.getSql(); statement.addBatch(sql); } @Override protected Statement instantiateStatement (Connection connection) throws SQLException { if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.createStatement(); } else { return connection.createStatement( mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY ); } } @Override public void parameterize (Statement statement) { } }
特点与适用场景
优点
缺点
适用场景
无需参数绑定,执行逻辑简单
不支持参数,SQL 需硬编码(如 WHERE id=1),易引发 SQL 注入
无参数的静态 SQL(如查询系统配置)
无预编译开销(适合一次性执行的简单 SQL)
不支持批量参数化(批量执行需重复拼接 SQL)
简单查询 / 测试场景
(2)PreparedStatementHandler:预编译 SQL 执行器 对应 JDBC 的 PreparedStatement,支持参数占位符(?) ,是 MyBatis 最常用的执行器(默认)。核心优势:预编译 SQL 可复用、防 SQL 注入、支持参数绑定。
核心源码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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); } @Override public int update (Statement statement) throws SQLException { PreparedStatement ps = (Statement) statement; ps.execute(); int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); mappedStatement.getKeyGenerator().processAfter(executor, mappedStatement, ps, parameterObject); return rows; } @Override public <E> List<E> query (Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps); } @Override public void batch (Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.addBatch(); } @Override protected Statement instantiateStatement (Connection connection) throws SQLException { String sql = boundSql.getSql(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); if (keyGenerator instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); return keyColumnNames == null ? connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : connection.prepareStatement(sql, keyColumnNames); } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.prepareStatement(sql); } else { return connection.prepareStatement( sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY ); } } @Override public void parameterize (Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } }
核心优势:
预编译复用 :相同 SQL 只需预编译一次,后续执行仅需绑定参数,提升性能;
防 SQL 注入 :参数通过 PreparedStatement.setXxx() 传递,避免 SQL 拼接导致的注入风险;
参数灵活 :支持复杂参数类型(如实体、Map、数组),通过 ParameterHandler 自动转换。
适用场景:
几乎所有带参数的 SQL(如 SELECT * FROM user WHERE id=?);
高频重复执行的 SQL(如商品详情查询);
批量操作(配合 BatchExecutor 批量提交)。
(3)CallableStatementHandler:存储过程执行器 对应 JDBC 的 CallableStatement,专门用于调用数据库存储过程 ,支持 IN(输入参数)、OUT(输出参数)、INOUT(输入输出参数)类型,需手动注册输出参数。
核心源码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class CallableStatementHandler extends BaseStatementHandler { public CallableStatementHandler (Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super (executor, ms, parameter, rowBounds, resultHandler, boundSql); } @Override public int update (Statement statement) throws SQLException { CallableStatement cs = (CallableStatement) statement; cs.execute(); int rows = cs.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); mappedStatement.getKeyGenerator().processAfter(executor, mappedStatement, cs, parameterObject); resultSetHandler.handleOutputParameters(cs); return rows; } @Override public <E> List<E> query (Statement statement, ResultHandler resultHandler) throws SQLException { CallableStatement cs = (CallableStatement) statement; cs.execute(); List<E> resultList = resultSetHandler.handleResultSets(cs); resultSetHandler.handleOutputParameters(cs); return resultList; } @Override protected Statement instantiateStatement (Connection connection) throws SQLException { String sql = boundSql.getSql(); return mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareCall(sql) : connection.prepareCall( sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY ); } @Override public void parameterize (Statement statement) throws SQLException { CallableStatement cs = (CallableStatement) statement; registerOutputParameters(cs); parameterHandler.setParameters(cs); } private void registerOutputParameters (CallableStatement cs) throws SQLException { List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); for (int i = 0 ; i < parameterMappings.size(); i++) { ParameterMapping pm = parameterMappings.get(i); if (pm.getMode() == ParameterMode.OUT || pm.getMode() == ParameterMode.INOUT) { JdbcType jdbcType = pm.getJdbcType(); if (jdbcType == null ) { throw new ExecutorException("OUT 参数必须指定 JdbcType:" + pm.getProperty()); } if (pm.getNumericScale() != null && (jdbcType == JdbcType.NUMERIC || jdbcType == JdbcType.DECIMAL)) { cs.registerOutParameter(i + 1 , jdbcType.TYPE_CODE, pm.getNumericScale()); } else if (pm.getJdbcTypeName() != null ) { cs.registerOutParameter(i + 1 , jdbcType.TYPE_CODE, pm.getJdbcTypeName()); } else { cs.registerOutParameter(i + 1 , jdbcType.TYPE_CODE); } } } } }
关键细节:
存储过程语法 :SQL 需用 {CALL 存储过程名(?, ?)} 格式(如 {CALL get_user(#{id,mode=IN}, #{name,mode=OUT,jdbcType=VARCHAR})});
输出参数注册 :必须通过 registerOutParameter() 告知 JDBC 输出参数的索引和类型,否则无法获取输出值;
结果处理 :存储过程可能返回多个结果集(ResultSet)和输出参数,需通过 handleOutputParameters() 单独处理输出参数。
适用场景:
调用数据库存储过程(如复杂的业务逻辑封装在存储过程中);
需要获取存储过程输出参数的场景(如计算结果、状态码)。
3. RoutingStatementHandler:执行器路由(策略模式) RoutingStatementHandler 本身不直接执行 SQL,而是根据 MappedStatement 的 statementType 属性,动态选择对应的 delegate 执行器 (Simple/Prepared/Callable),是策略模式的典型应用。
核心源码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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("未知 statementType:" + ms.getStatementType()); } } @Override public Statement prepare (Connection connection, Integer transactionTimeout) throws SQLException { return delegate.prepare(connection, transactionTimeout); } @Override public void parameterize (Statement statement) throws SQLException { delegate.parameterize(statement); } @Override public int update (Statement statement) throws SQLException { return delegate.update(statement); } }
核心价值:
解耦选择逻辑 :上层(如 Executor)无需关心具体执行器的创建,只需通过 statementType 配置即可切换;
简化扩展 :若新增执行器(如自定义 Statement 类型),只需在 switch 中添加分支,符合开闭原则。
StatementHandler 调用链路:与 Executor 的协作 StatementHandler 并非独立工作,而是由 Executor(执行器)调度 ,形成 “Executor→StatementHandler→ParameterHandler/ResultSetHandler” 的完整调用链路。以 SimpleExecutor.doQuery() 为例,拆解调用流程:
调用链路源码追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public <E> List<E> doQuery (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null ; try { Configuration config = ms.getConfiguration(); StatementHandler handler = config.newStatementHandler(this .wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement (StatementHandler handler, Log statementLog) throws SQLException { Connection conn = getConnection(statementLog); Statement stmt = handler.prepare(conn, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
完整流程梳理 Executor(执行器) RoutingStatementHandler(路由) PreparedStatementHandler(具体执行器) ParameterHandler(参数处理器) ResultSetHandler(结果处理器) 数据库 Executor RoutingStatementHandler PreparedStatementHandler ParameterHandler ResultSetHandler 创建实例(传入 statementType) 选择 delegate(PREPARED) 调用 prepare() 创建 PreparedStatement(预编译 SQL) 设置超时/fetchSize 调用 parameterize() 委托 setParameters() 为 ? 绑定参数 调用 query() 执行 SQL,返回 ResultSet 委托 handleResultSets() 解析 ResultSet 为 Java 对象 返回结果列表 关闭 Statement Executor(执行器) RoutingStatementHandler(路由) PreparedStatementHandler(具体执行器) ParameterHandler(参数处理器) ResultSetHandler(结果处理器) 数据库 Executor RoutingStatementHandler PreparedStatementHandler ParameterHandler ResultSetHandler
实战配置与最佳实践 1. 配置 statementType:选择执行器 在 Mapper XML 或注解中通过 statementType 属性指定执行器,默认值为 PREPARED:
statementType
对应执行器
适用场景
STATEMENT
SimpleStatementHandler
无参数的静态 SQL
PREPARED
PreparedStatementHandler
带参数的预编译 SQL(默认)
CALLABLE
CallableStatementHandler
调用存储过程
配置示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="selectAllUser" resultType ="user" statementType ="STATEMENT" > SELECT * FROM user </select > <select id ="selectUserById" resultType ="user" statementType ="PREPARED" > SELECT * FROM user WHERE id = #{id} </select > <select id ="callGetUser" statementType ="CALLABLE" resultType ="user" > {CALL get_user(#{id,mode=IN,jdbcType=INTEGER}, #{name,mode=OUT,jdbcType=VARCHAR})} </select >
2. 常见问题与解决方案 (1)存储过程输出参数无法获取
(2)批量执行效率低
(3)SQL 注入风险
问题原因 :使用 STATEMENT 执行带参数的 SQL(如 WHERE name = '${name}');
解决方案 :切换为 PREPARED,使用 #{} 占位符(自动防注入),避免 ${} 直接拼接 SQL。
总结 StatementHandler 是 MyBatis 数据库交互的 “核心执行者”,其设计体现了 MyBatis 对 JDBC 的优雅封装:
架构设计 :通过模板方法模式(BaseStatementHandler)封装通用逻辑,策略模式(RoutingStatementHandler)动态选择执行器,降低耦合,提升扩展性;
核心实现 :三个具体执行器对应 JDBC 三种 Statement,覆盖无参数、预编译、存储过程场景,满足不同业务需求;
协作关系 :与 Executor、ParameterHandler、ResultSetHandler 紧密协作,形成 “SQL 执行→参数设置→结果映射” 的完整闭环
v1.3.10