Mybatis从SqlSession到Executor再到Statement,这就是一条SQL执行的调用过程,而Statement接口就是数据库底层的统一对外接口,不同数据库厂商自定义的驱动中就包括实现这个接口。
而Mybatis并没有直接从Executor访问Statement,中间还套了一层StatementHandler。
1、StatementHandler概览
StatementHandler跟Executor的继承实现很像,都有一个Base,Base下面又有几个具体实现子类,这又是模板模式的应用。看下另一个Routing就不同于CacheExecutor用于二级缓存之类的实际作用了,仅用于维护三个Base子类的创建与调用。
-
BaseStatementHandler
-
SimpleStatementHandler:JDBC中的Statement接口,处理简单SQL的
-
CallableStatementHandler:JDBC中的PreparedStatement,预编译SQL的接口
-
PreparedStatementHandler:JDBC中的CallableStatement,用于执行存储过程相关的接口
-
-
RoutingStatementHandler:路由三个Base子类,负责其创建及调用
看下BaseStatementHandler的属性
// 1.配置
protected final Configuration configuration;
// 2.对象处理工厂
protected final ObjectFactory objectFactory;
// 3.类型处理
protected final TypeHandlerRegistry typeHandlerRegistry;
// 4.结果集处理
protected final ResultSetHandler resultSetHandler;
// 5.参数处理
protected final ParameterHandler parameterHandler;
// 6.具体执行器
protected final Executor executor;
// 7.具体SQL映射对象
protected final MappedStatement mappedStatement;
// 8.执行SQL条数参数
protected final RowBounds rowBounds;
// 9.具体SQL信息
protected BoundSql boundSql;
2、StatementHandler解析
因为涉及到了具体操作数据库,就是由三个BaseExecutor的子类自己实现,不过三个的逻辑大体一致,分几个步骤
2.1 创建StatementHandler
@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();
// here
// here
// here
// 创建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
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) {
// 1.创建StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 2.插件处理
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
- 创建StatementHandler
- 插件处理(责任链模式)
看下 RoutingStatementHandler 的构造方法是如何创建StatementHandler的
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("Unknown statement type: " + ms.getStatementType());
}
}
嗯,很眼熟的策略模式,按照statementType的值来决定返回哪种StatementHandler。
statementType的取值?构造方法中默认取值为PREPARED
mappedStatement.statementType = StatementType.PREPARED;
可以通过<select />的statementType属性指定
<select id="getAll" resultType="Student2" statementType="CALLABLE">
SELECT * FROM Student
</select>
或通过@SelectKey的statementType属性指定
@SelectKey(keyProperty = "account",
before = false,
statementType = StatementType.STATEMENT,
statement = "select * from account where id = #{id}",
resultType = Account.class)
Account selectByPrimaryKey(@Param("id") Integer id);
2.2 创建Statement
然后就是创建并处理Statement,用于与JDBC交互了,三个Executor的逻辑基本一致,分三步
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 1.拿到连接
Connection connection = getConnection(statementLog);
// 2.创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 3.预编译
handler.parameterize(stmt);
return stmt;
}
- 拿到连接
- 创建Statement对象(ReuseExecutor会先去缓存中查看是否存在,不存在再创建)
- 预编译
然后是SimpleExecutor、ReuseExecutor就是直接执行了,而BatchExecutor就得存起来等一次批量执行的调用了。
BaseStatementHandler抽象类只有一个抽象方法 instantiateStatement 方法,是在预编译的时候调用,用于获取Statement对象,由子类实现
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
SimpleStatementHandler、CallableStatementHandler比较简单直接获取
@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);
}
}
- 直接通过createStatement拿到Statement对象
而PrepareStatementHandler则比较复杂
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
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() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
- 通过prepareStatement预编译方法拿到PrepareStatement
下面以查询为例简单看下三个Statement的区别。
2.3 SimpleStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
对于SimpleStatementHandler来说,就是简单执行SQL后,将结果集转化成list返回。
2.4 PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
将Statement转化为PreparedStatement后执行,将结果集转化成list返回。
2.5 CallableStatementHandler
@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;
}
将Statement转化为CallableStatement后进行操作,处理存储过程的输出并将结果集转化成list返回。
2.6 ResultSetHandler
简单看下结果集处理接口ResultSetHandler
public interface ResultSetHandler {
// 将结果集转化成list
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 将结果集转化出呢个cursor
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 处理存储过程的输出
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
这个接口只有一个默认实现DefaultResultSetHandler,这个类的方法着实有点多。。
DefaultResultSetHandler-method
嗯,有需要再来深究。
总结
- Mybatis通过StatementHandler来处理与JDBC的交互
- Statement是具体与数据库底层打交道的接口,不同数据库厂商需要自己定义实现,比如MySQL、Oracle等
网友评论