正文
{
* 处理 Statement 对象并返回结果对象
*
*
@param
stmt SQL 语句执行后返回的 Statement 对象
*
@return
映射后的结果对象列表
*/
List
handleResultSets
(Statement stmt)
throws
SQLException;
* 处理 Statement 对象并返回一个 Cursor 对象
* 它用于处理从数据库中获取的大量结果集,与传统的 List 或 Collection 不同,Cursor 提供了一种流式处理结果集的方式,
* 这在处理大数据量时非常有用,因为它可以避免将所有数据加载到内存中
*
*
@param
stmt SQL 语句执行后返回的 Statement 对象
*
@return
游标对象,用于迭代结果集
*/
Cursor
handleCursorResultSets
(Statement stmt)
throws
SQLException;
* 处理存储过程的输出参数
*
*
@param
cs 存储过程调用的 CallableStatement 对象
*/
void
handleOutputParameters
(CallableStatement cs)
throws
SQLException;
}
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException;
Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
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;
List query(Statement statement, ResultHandler resultHandler) throws SQLException;
Cursor queryCursor(Statement statement) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
为了加深大家对这四个处理器的理解,了解它在查询 SQL 执行时作用的时机,我们来看一下查询 SQL 执行时的流程图:
每个声明 SQL 查询语句的 Mapper 接口都会被
MapperProxy
代理,接口中每个方法都会被定义为
MapperMethod
对象,借助
PlainMethodInvoker
执行(动态代理模式和策略模式),
MapperMethod
中组合了
SqlCommand
和
MethodSignature
,
SqlCommand
对象很重要,它的
SqlCommand#name
字段记录的是
MappedStatement
对象的 ID 值(eg: org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor),根据它来获取唯一的
MappedStatement
(每个
MappedStatement
对象对应 XML 映射文件中一个
,
,
, 或
标签定义),
SqlCommand#type
字段用来标记 SQL 的类型。当方法被执行时,会先调用
SqlSession
中的查询方法
DefaultSqlSession#selectOne
,接着由
执行器
Executor
去承接,默认类型是
CachingExecutor
,注意在这里它会调用
MappedStatement#getBoundSql
方法获取
BoundSql
对象,这个对象实际上最终都是在
StaticSqlSource#getBoundSql
方法中获取的,也就是说
此时我们定义在 Mapper 文件中的 SQL 此时已经被解析、处理好了(动态标签等内容均已被处理)
,保存在了
BoundSql
对象中。此时,要执行的 SQL 已经准备好了,它会接着调用
SQL 处理器
的
StatementHandler#prepare
方法创建与数据库交互的
Statement
对象,其中记录了要执行的 SQL 信息 ,而封装 SQL 的执行参数则由
参数处理器
DefaultParameterHandler
和
TypeHandler
完成,
ResultSet
结果的处理:将数据库中数据转换成所需要的 Java 对象由
结果处理器
DefaultResultSetHandler
完成。
现在我们对拦截器的原理和查询 SQL 的执行流程已经有了基本的了解,回过头来再想一下我们的需求:“使用 Mybatis 的拦截器在 SQL 执行前进行打标”,那么我们该选择哪个方法作为切入点更合适呢?
理论上来说在
Executor
,
StatementHandler
和
ParameterHandler
相关的方法中切入都可以,但实际上我们还要多考虑一步:
ParameterHandler
是用来处理参数相关的,在这里切入一般我们是要对入参 SQL 的入参进行处理,所以不选择这里避免为后续同学维护时增加理解成本;
Executor
“有时不是很合适”,它其中有两个
query
方法,先被执行的方法,
对应图中
CacheExecutor
左侧的直线
query
方法
:
Executor#query(MappedStatement, Object, RowBounds, ResultHandler)