在MyBatis中,StatementHandler负责创建Statement对象并执行SQL语句。以下是其具体流程:
1. StatementHandler.prepare()
方法:创建 JDBC Statement
对象
StatementHandler.prepare(Connection connection, Integer transactionTimeout)
方法是核心方法,负责基于 MappedStatement
配置创建相应的 JDBC Statement
对象。
以下是 prepare()
方法内部发生步骤的分解:
输入参数:
Connection connection
:将用于创建Statement
的 JDBCConnection
对象。此Connection
通常由 MyBatis 的Transaction
组件管理。Integer transactionTimeout
:一个可选的事务超时值(以秒为单位),用于Statement
。
确定
Statement
类型:
StatementHandler
检查其持有的MappedStatement
对象以确定statementType
。statementType
在 Mapper XML 文件中通过<select>
、<insert>
、<update>
、<delete>
元素中的statementType
属性进行配置。可能的取值包括:STATEMENT
:用于普通的java.sql.Statement
。PREPARED
:用于java.sql.PreparedStatement
(默认且最常用)。CALLABLE
:用于java.sql.CallableStatement
(用于存储过程/函数)。
创建 JDBC
Statement
对象:
基于确定的statementType
,StatementHandler
使用提供的Connection
对象来创建相应的 JDBCStatement
实例:statementType="STATEMENT"
(SimpleStatementHandler
):Statement statement = connection.createStatement(); // 使用 Connection.createStatement()
SimpleStatementHandler
使用connection.createStatement()
创建一个普通的Statement
对象。 这是最简单的类型,适用于没有参数的基本 SQL。statementType="PREPARED"
(PreparedStatementHandler
- 默认):PreparedStatement statement = connection.prepareStatement(boundSql.getSql()); // 使用 Connection.prepareStatement(sql)
PreparedStatementHandler
使用connection.prepareStatement(boundSql.getSql())
。 这至关重要:connection.prepareStatement(boundSql.getSql())
: 它使用Connection.prepareStatement()
创建一个PreparedStatement
。 重要的是,它将boundSql.getSql()
作为 SQL 字符串传递给prepareStatement()
。boundSql.getSql()
包含最终的、处理后的 SQL 语句,在动态 SQL 评估之后,参数占位符(?
)已就位。- PreparedStatement 用于性能和安全性:
PreparedStatement
用于提高性能(数据库预编译)和安全性(通过使用参数占位符防止 SQL 注入)。
statementType="CALLABLE"
(CallableStatementHandler
):CallableStatement statement = connection.prepareCall(boundSql.getSql()); // 使用 Connection.prepareCall(sql)
CallableStatementHandler
使用connection.prepareCall(boundSql.getSql())
创建一个CallableStatement
,同样传递处理后的 SQL (boundSql.getSql()
),这将是存储过程/函数的调用语法。
设置查询超时(可选):
如果提供了transactionTimeout
值(可以在 MyBatis 配置中配置),StatementHandler
将其设置为新创建的Statement
的查询超时时间:if (transactionTimeout != null) { statement.setQueryTimeout(transactionTimeout); // 设置 JDBC Statement 超时时间 }
这有助于防止长时间运行的查询无限期挂起并消耗资源。
返回
Statement
对象:
最后,prepare()
方法返回创建的 JDBCStatement
对象(Statement
、PreparedStatement
或CallableStatement
)。 这个Statement
对象现在已准备好进行参数化和执行。
2. StatementHandler
执行 SQL 语句的方法
一旦 Statement
对象准备就绪,StatementHandler
提供了实际执行 SQL 的方法:query()
和 update()
(以及 BatchExecutor
的 batch()
):
StatementHandler.query(Statement statement, ResultHandler resultHandler)
:执行SELECT
查询输入参数:
Statement statement
:JDBCStatement
对象(在prepare()
中创建)。ResultHandler resultHandler
:负责处理ResultSet
的ResultHandler
。
执行查询:
statement.executeQuery(); // 使用 Statement.executeQuery()
StatementHandler
调用statement.executeQuery()
来执行 SQL 查询。executeQuery()
专门用于SELECT
语句,并返回ResultSet
。处理
ResultSet
:ResultSet resultSet = statement.getResultSet(); resultHandler.handleResultSets(resultSet); // 将 ResultSet 处理委托给 ResultHandler
statement.getResultSet()
:检索查询生成的ResultSet
。resultHandler.handleResultSets(resultSet)
: 至关重要的是,StatementHandler
将ResultSet
的实际处理和映射委托给ResultHandler
。ResultHandler
迭代ResultSet
,并使用ResultMap
(或resultType
)将每一行映射到 Java 对象。
返回结果列表:
query()
方法返回ResultHandler
在ResultSet
处理期间填充的List<E>
。
StatementHandler.update(Statement statement)
:执行INSERT
、UPDATE
、DELETE
语句输入参数:
Statement statement
:JDBCStatement
对象。
执行更新:
statement.executeUpdate(); // 使用 Statement.executeUpdate()
StatementHandler
调用statement.executeUpdate()
来执行INSERT
、UPDATE
或DELETE
语句。executeUpdate()
用于 DML(数据操作语言)语句,这些语句修改数据并返回受影响的行数。返回更新计数:
return statement.getUpdateCount(); // 获取受影响的行数
update()
方法返回statement.getUpdateCount()
返回的整数值,该值表示INSERT
、UPDATE
或DELETE
操作影响的行数。
StatementHandler.batch(Statement statement)
:执行批量语句(用于BatchExecutor
)输入参数:
Statement statement
:JDBCStatement
对象。
添加到批处理(多次调用):
对于BatchExecutor
,在调用batch()
之前,会处理多个 SQL 语句(由BoundSql
对象表示)。 对于批处理中的每个 SQL 语句,都会调用parameterize()
方法来设置Statement
上的参数,然后:preparedStatement.addBatch(); // 添加到 JDBC 批处理
BatchExecutor
使用preparedStatement.addBatch()
将PreparedStatement
添加到 JDBC 批处理中。执行批处理(调用一次):
在将所有 SQL 语句添加到批处理之后,调用batch()
方法:statement.executeBatch(); // 执行 JDBC 批处理
StatementHandler
调用statement.executeBatch()
以在单个数据库往返中执行批处理队列中的所有批处理 SQL 语句。返回批处理结果:
return statement.getBatchResult(); // 获取批处理结果
batch()
方法检索批处理执行的结果(通常是更新计数或异常的数组),并使用statement.getBatchResult()
将其作为List<BatchResult>
返回。
总结:
StatementHandler
充当 MyBatis 和原始 JDBC API 之间的桥梁。
- 创建不同类型的
Statement
对象(Statement
、PreparedStatement
、CallableStatement
),基于MappedStatement
配置,使用 JDBCConnection
方法(createStatement()
、prepareStatement()
、prepareCall()
)。 - 使用
Statement.executeQuery()
执行 SQL 查询,并将ResultSet
处理委托给ResultHandler
。 - 使用
Statement.executeUpdate()
执行 SQL 更新,并返回更新计数。 - 使用
Statement.addBatch()
和Statement.executeBatch()
执行批处理 SQL 语句(在BatchExecutor
中)。