Mybatis源码分析--StatementHandler源码分析
1 概述
在上一篇文章(Mybatis源码分析--Executor源码分析),我们分析Executor源码的时候就提到了StatementHandler的作用是和数据库对话。当然StatementHandler和数据库对话是依赖于Statement来完成的。在这里随便说一下ParameterHandler和ResultHandler的作用分别是绑定SQL参数和组装最后的结果返回。
2 生成StatementHandler
StatementHandler对象的生成是通过调用Configuration的newStatementHandler函数来完成的。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//调用RoutingStatementHandler的构造函数来生成StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
从上面的源码我们可想而知,针对StatementHandler对象的生成完全交给了RoutingStatementHandler的构造函数。至于RoutingStatementHandler和StatementHandler的关系,我们下面先来看一看StatementHandler的UML类图。
3 StatementHandler的结构
首先我们直接来看一下StatementHandler的UML类图。
我们再来看一下RoutingStatementHandler的源码。
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//通过Statement的类型来构造对应的StatementHandler
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());
}
}
@Override
public Statement prepare(Connection connection) throws SQLException {
return delegate.prepare(connection);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
@Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
}
@Override
public int update(Statement statement) throws SQLException {
return delegate.update(statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
@Override
public BoundSql getBoundSql() {
return delegate.getBoundSql();
}
@Override
public ParameterHandler getParameterHandler() {
return delegate.getParameterHandler();
}
}
查看上面的源码,我们可以发现RoutingStatementHandler根据不同的Statement类型持有相应的StatementHandler,RoutingStatementHandler的所有函数都是依赖于具体的StatementHandler的具体函数来实现的。这里相当于一个什么设计模式呢?相当于一个策略模式,而RoutingStatementHandler就相当于一个策略容器,StatemetnHandler是抽象策略类,而BaseStatementHandler的子类SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler就是具体策略类。
接下来,我们来看一下StatemetnHandler接口的各个实现类的具体作用。
(1)RoutingStatementHandler:这是一个封装类,不提供任何具体的实现,只是根据不同的Executor来创建不同的StatementHandler,然后依赖于具体的statementHandler来实现不同的方法。其实这就相当于一个策略模式的策略容器。
(2)SimpleStatementHandler:这个类对应于JDBC的Statement对象,用于没有预编译的SQL的运行。
(3)PreparedStatementHandler :这个用于预编译参数的SQL运行。
(4)CallableStatementHandler :用于存储过程的调用。
接下来我们来看一看StatementHandler各个函数的作用及部分函数的逻辑。
4 BaseStatementHandler
BaseStatementHandler里面实现的函数其实就是做好准备工作,我们来看prepare函数的逻辑。
/**
* 传入Connection,创建并初始化Statement
*/
@Override
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//初始化statement
statement = instantiateStatement(connection);
//设置查询超时时间
setStatementTimeout(statement);
//设置每次允许传递到数据库的查询语句条数
setFetchSize(statement);
return statement;
} catch (SQLException e) {
//如果发生异常则关闭Statement
closeStatement(statement);
throw e;
} catch (Exception e) {
//如果发生异常则关闭Statement
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
针对具体的StatemetnHandler我们来看一下SimpleStatementHandler的实现就行,至于其余的StatementHandler函数的实现其实就是调用不同的Statement(StatementImpl、PreparedStatement、CallableStatement)来实现不同类型的数据库操作而已。
5 SimpleStatementHandler
(1)update函数
@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) {
//指定sql语句
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;
}
(2)batch函数
@Override
public void batch(Statement statement) throws SQLException {
String sql = boundSql.getSql();
//执行statement的批量添加
statement.addBatch(sql);
}
(3)query函数
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
//这里需要使用到ResultHandler来处理查询结果
return resultSetHandler.<E>handleResultSets(statement);
}
(4)instantiateStatement函数
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
//使用Connection的createStatement函数创建Statement对象
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
(5)parameterize函数
//参数预处理,只有PrepareStatementHandler类才有这个函数的具体实现
@Override
public void parameterize(Statement statement) throws SQLException {
// N/A
}
上面的类容就是我们对StatementHandler的分析,接下来我们将要继续分析Mybatis绑定参数和处理执行结果的Handler,ParameterHandler和ResultHandler。欢迎交流。