Executor是Mybatis核心接口之一,定义了数据库操作最基础的方法,SqlSession都是基于它来实现的。Executor包位于org.apache.ibatis.executor,这里采用模板模式实现。
模板模式
模板模式(Template Pattern):一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。定义了一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变算法的结构即可重定义该算法的某些实现。
当我们遇到由一系列步骤构成的过程需要执行,这个过程从高层次看是相同的,但是具体有些步骤的实现不同,这个时候就需要考虑用模板模式了。
Executor模块
BaseExecutor:实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,而其他子类只需要实现doUpdate,doQuery等方法。
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
SimpleExecutor:默认配置,使用statement对象访问数据库,每次访问都要创建新的statement对象。
@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();//获取configuration对象
//创建StatementHandler对象,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象的动态代理,添加日志能力;
Connection connection = getConnection(statementLog);
//通过不同的StatementHandler,利用connection创建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
ReuseExecutor:使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象。
private final Map<String, Statement> statementMap = new HashMap<>();
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();//获取configuration对象
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
return handler.<E>query(stmt, resultHandler);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();//获取sql语句
if (hasStatementFor(sql)) {//根据sql语句检查是否缓存了对应的Statement
stmt = getStatement(sql);//获取缓存的Statement
applyTransactionTimeout(stmt);//设置新的超时时间
} else {//缓存中没有statment,创建statment过程和SimpleExecutor类似
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);//放入缓存中
}
//使用parameterHandler处理占位符
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
BatchExecutor:实现批量执行多条sql语句。
private final List<Statement> statementList = new ArrayList<>();
private final List<BatchResult> batchResultList = new ArrayList<>();
private String currentSql;
private MappedStatement currentStatement;
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
// handler.parameterize(stmt);
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
通过对SimpleExecutor的doQuery()的解读发现,Executor的核心功能,需要调度以下三个类:
StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作。
//从连接中获取一个Statement
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
//占位符处理,绑定statement执行时需要的实参
void parameterize(Statement statement)
throws SQLException;
//批量执行sql语句
void batch(Statement statement)
throws SQLException;
//执行update/insert/delete语句
int update(Statement statement)
throws SQLException;
//执行select语句
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
//获取sql语句
BoundSql getBoundSql();
//获取封装的参数处理器
ParameterHandler getParameterHandler();
ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的占位符都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及相关属性。
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
ResultSetHandler:对数据库返回的结果集进行封装,返回指定的实体类型。
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
StatementHandler分析
StatementHandler:是实现Executor的基础,这里也采用了模板模式实现。
BaseStatementHandler:所有子类的抽象父类,定义了初始化statement的顺序,由子类具体实例化不同的statement。
@Override
//使用模板模式,定义了获取Statement的步骤,其子类实现实例化Statement的具体的方式;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//通过不同的子类实例化不同的Statement,分为三类:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler)
statement = instantiateStatement(connection);
//设置超时时间
setStatementTimeout(statement, transactionTimeout);
//设置数据集大小
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
RoutingStatementHandler:Executor组件真正实例化的子类,使用静态代理模式,根据上下文决定具体创建哪个实体类。
private final StatementHandler delegate;//底层封装的真正的StatementHandler对象
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler最主要的功能就是根据mappedStatment的配置,生成一个对应的StatementHandler对象并赋值给delegate
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());
}
}
SimpleStatementHandler:使用statement对象访问数据库,无须参数化。
@Override
//使用底层的statment对象来完成对数据库的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
//设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
PreparedStatementHandler:使用预编译PreparedStatement对象访问数据库。
@Override
//使用底层的prepareStatement对象来完成对数据库的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
//根据mappedStatement.getKeyGenerator字段,创建prepareStatement
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {//对于insert语句
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//返回数据库生成的主键
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
//返回数据库生成的主键填充至keyColumnNames中指定的列
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//创建普通的prepareStatement对象
return connection.prepareStatement(sql);
}
}
CallableStatementHandler:调用存储过程。