前文大致描述了Mybatis的构建阶段,下面我们来说说Mybatis的执行阶段:
1.Mybatis执行期间的关键类 (大致了解熟悉即可) :
SqlSession:Mybatis的核心执行入口,默认实现为DefaultSqlSession,提供了大量SQL调用方法
Configuration:保存构建阶段的结果,也负责执行阶段初始化所需要的变量
MappedStatement:配置好的映射SQL语句
BoundSql:存放SQL的执行内容
Executor:执行器,用于执行和数据库交互的组件,Mybatis有三种执行器,分别为:
(1) SIMPLE:简易执行器,不配置则为默认执行器
(2) REUSE:可以重用预处理语句
(3) BATCH:重用语句和批量更新
StatementHandler:处理SQL语句的管理器接口
ResultHandler:处理结果集
2.具体的执行流程:
public void test throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
}
这段代码大家应该十分熟悉,前文讲解了步骤1的执行流程,下面我们进入到流程2:
//调用openSession()会调用下面的构造函数创建实例
public SqlSession openSession() {return
this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
//生成需要使用的SqlSession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
//取出构建好的环境
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//生成执行器,很关键的一步,后文会在这继续深入下去
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
在上面的源码中有初始化Executor实例这一步,下面我们来仔细看看这一步都做了哪些工作:
//根据配置生成对应的Executor实例
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
//前文描述过的三种执行器都出现在这里
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {
//支持缓存的执行器
executor = new CachingExecutor((Executor)executor);
}
//装载插件
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
到了这里,SqlSession及其重要属性executor和configuration均已经构造完成,下面我们回到DefaultSqlSession这个类,看看这个类究竟是如何执行SQL语句查询的:
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
//DefaultSqlSession的构造函数
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
//这是一个用于查询的方法,该类中还有其它类似的方法,本文以该方法为例子进行讲解
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//下文是十分重要的两个步骤
//1从构建好的configuration对象中获取MappedStatment对象
MappedStatement ms = this.configuration.getMappedStatement(statement);
//2调用之前构建好的执行器执行query查询
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
SqlSession的查询接口共有5大类查询方法,具体可以参照源码,这里以selectList()为例继续探究SQL执行流程:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
//获取已经初始化的配置
Configuration configuration = ms.getConfiguration();
//创建StatementHandler对象,构建语句处理器
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//一些准备工作
stmt = this.prepareStatement(handler, ms.getStatementLog());
//真正的查询工作是在这一步
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
//预处理statementHandler,准备好执行环境
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//获取连接,进行动态代理
Connection connection = this.getConnection(statementLog);
//初始化日志组件,初始化执行语句,设置超时时间等等
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
//对参数进行处理
handler.parameterize(stmt);
return stmt;
}
//在executor对象在执行具体的SQL时,通过Configuration类来创建statementHandler对象
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
在doQuery方法中,构建了语句处理器,并完成了对其的初始化工作,下一步则真正进入到查询工作:
public int update(Statement statement) throws SQLException {
String sql = this.boundSql.getSql();
Object parameterObject = this.boundSql.getParameterObject();
KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, 1);
rows = statement.getUpdateCount();
keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
//真正的执行工作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//获取SQL语句的具体内容
String sql = this.boundSql.getSql();
//到这调用jdbc进行查询
statement.execute(sql);
//使用结果处理器堆查询结果进行加工处理
return this.resultSetHandler.handleResultSets(statement);
}
直到上述代码调用jdbc进行查询,在对结果进行加工处理,到这里,Mybatis的查询流程大体上就讲清楚了,下面来总结一下Mybatis的查询流程:
1 根据配置文件(全局,sql映射)初始化出Configuration对象
2 创建一个DefaultSqlSession对象,里面包含Configuration以及 Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
3 DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
4 MapperProxy里面有(DefaultSqlSession);
5 执行增删改查方法:
1) 调用DefaultSqlSession的增删改查(Executor);
2) 创建StatementHandler,ParameterHandler和ResultSetHandler等加工处理对象
3) 调用StatementHandler预编译参数以及设置参数值; 使用ParameterHandler来给sql设置参数
4) 调用StatementHandler的增删改查方法;
5) ResultSetHandler封装结果
至于时序图和类图我有时间的话会补上的,最近没有时间画,顺带推荐Visual Paradigm画图,真的顺手。