一次insert操作过程
以保存一条记录到表中这个简单的操作为例,就按这个例子来跟踪mybatis是如何执行sql语句的,要保存一个user记录到表中:
sqlSession.insert("x.y.insertUser", user);
首先当然是要看看SqlSession#insert
方法,到DefaultSqlSession
这个实现类中查找具体的实现,下面两个代码片段是insert方法实现中的调用链:
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
第一段代码中显示insert
方法中调用到了update
方法,通过update
方法实现插入操作,另外delete
方法实际上最后也是调用的update
方法完成移除操作的。
接着第二段代码,在update方法内部,首先获取了一个MappedStatement
对象,这个对象实际上封装的就是mapper映射文件中我们配置的各个insert、delete、update的节点的信息,它在mybatis加载Configuration对象时就解析好了。
接着通过一个executor对象完成update操作,executor是DefaultSqlSession中持有的一个Executor
类型的引用,在初始化DefaultSqlSession时就设置了这个引用。Executor
是mybatis的底层核心组件,我们知道SqlSession提供了一套面向用户的方法,然后这些方法的底层都是通过executor来提供实际能力的,Executor定义了一套和SqlSession很类似的操作方法,有点像外观模式。
MyBatis内部提供了多种Executor的具体实现,下面是它的体系结构图:
默认MyBatis使用的是SimpleExecutor
,如何使用其他类型的Executor之后的说明中会提到。因此要看一下SimpleExector中update的实现,但是在BaseExecutor中有一个基本实现,然后通过doUpdate
这个模板方法实现到具体子类的调用:
// BaseExecutor:
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
// 通过doUpdate抽象方法调用具体子类的实现
return doUpdate(ms, parameter);
}
// SimpleExecutor:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
如上所示,在SimpleExecutor的doUpdate方法实现中,通过configuration对象创建了一个StatementHandler
,这里StatementHandler
是MyBatis内部另一个重要的核心对象,它是一个java接口,事实上对于数据库的操作是在StatementHandler组件中完成的,而Executor则像一个分发器,把不同种操作分发给StatementHandler对象处理,StatementHandler基本上实现了全部的操作过程:从sql语句创建Statement对象、替换sql参数占位符、查询、更新以及处理结果集等等,从下面贴出的StatementHandler接口定义可以看到这些操作:
public interface StatementHandler {
// 创建jdbc Statement对象
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 执行参数化sql的参数填充
void parameterize(Statement statement)
throws SQLException;
// 批量update操作,对应jdbc的addBatch操作
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 获取绑定的sql语句对象
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
针对PreparedStatement、CallableStatement以及Statement类型的sql,StatementHandler分别提供了具体的实现类,它的类扩展结构图如下:
图中的RoutingStatementHandler
和其他三种实现不同,它是一个具有分发功能的实现,把具体的请求转发到另外三种具体的StatementHandler,它里面持有一个其他特定类型的StatementHandler引用delegate
,根据不同的sql种类,把请求委托给具体的StatementHandler,看看RoutingStatementHandlerde的代码就清楚了:
// RoutingStatementHandler:
public class RoutingStatementHandler implements StatementHandler {
// 委托对象
private final StatementHandler delegate;
// 根据不同种类的sql,把delegate对象初始化为某种具体的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());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
// ... 省略其他StatementHandler的实现方法
}
现在再回到上面标注”SimpleExecutor:“处的代码片段:
首先,里面通过configuration创建的StatementHandler就是一个RoutingStatementHandler
;
第二步,通过prepareStatement方法获得一个Statement对象:
// SimpleExecutor#prepareStatement:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 在方法的底层从数据源获取Connection对象,并设置了事务提交属性和事务隔离级别
Connection connection = getConnection(statementLog);
// 通过刚才说的RoutingStatementHandler得prepare方法创建一个Statement对象;
// RoutingStatementHandler里面则委托给具体的StatementHandler,
// 如果sql mapper中没有特殊配置,那么应该是PreparedStatementHandler
stmt = handler.prepare(connection, transaction.getTimeout());
// 最后填充参数占位符
handler.parameterize(stmt);
return stmt;
}
此处是替换参数占位符的方法调用,PreparedStatementHandler中是通过一个ParameterHandler
实现的,PreparedStatementHandler还有一个ResultHandler
,这是用来处理查询结果集的。可以看到Executor通过StatementHandler、ParameterHandler、ResultHandler几个对象合作来完成整个数据库的操作。
第三步,调用了handler.update(stmt)
,根据上面的说明知道,这句调用委托到了PreparedStatementHandler的update方法,看看最终的update实现:
// PreparedStatementHandler#update:
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行语句
ps.execute();
// 获取影响行数
int rows = ps.getUpdateCount();
// 下面获取产生的key
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
到这里整个insert数据库操作过程完成了。
Executor的创建
前面说过DefaultSqlSession中的executor引用在初始化时设置引用值,这是通过DefaultSqlSessionFactory
创建的,以我们通常的创建一个SqlSession对象的例子为例:
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder.build(stream);
SqlSession sqlSession = sqlSessionFactory.open();
SqlSessionFactoryBuilder.build()方法里面创建的实际就是一个DefaultSqlSessionFactory类型的实例,下面我们跟踪DefaultSqlSessionFactory里的流程:
// DefaultSqlSessionFactory#openSession:
public SqlSession openSession() {
// 获取了默认的executor type
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// DefaultSqlSessionFactory#openSessionFromDataSource:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建一个executor对象
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象,把executor对象传递给它
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
下面看看Executor实例的创建代码:
// Configuration#newExecutor:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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 (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
newExecutor方法中根据参数executorType来创建具体类型的Executor实例,在openSession方法中executorType的值设置Configuration对象中的默认值,Configuration中默认设置的就是SIMPLE
:
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
因此,默认我们使用openSession()
或者openSession(autoCommit)
方法时内部得到的是SimpleExecutor
实例,当然我们可以修改默认值,Configuration类提供了defaultExecutorType的setter方法,对应到配置文件中设置defaultExecutorType
属性值为SIMPLE
,REUSE
或者BATCH
即可,另一种方式是直接使用SqlSession的其他重载形式:
sqlSession.openSession(ExecutorType.REUSE);
// 或者:
sqlSession.openSession(ExecutorType.REUSE, true);
几个重要类的依赖关系
下面是mybatis中几个重要类的依赖关系图,通过此图有助于理解mybatis是如何实现事物控制的。
来源:oschina
链接:https://my.oschina.net/u/3227308/blog/3224532