记录是一种精神,是加深理解最好的方式之一。
最近看了下Mybatis的源码,了解了下SqlSession执行Sql的过程,在这里把他记下来
曹金桂 cao_jingui@163.com(如有遗漏之处还请指教)
时间:2016年10月5日14:50
SqlSession的delete/update/insert执行过程
调用过程说明
- 用户代码获取到SqlSession对象后(DefaultSqlSession),调动SqlSession的insert/update/delete
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement); //获取MappedStatement对象,此对象包含了对应Mapper的所有配置信息
return executor.update(ms, wrapCollection(parameter)); //调用Executor对象的update方法
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
SqlSession中的Executor对象在Configuration中创建的
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//确保ExecutorType不为空(defaultExecutorType有可能为空)
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 根据ExecutorType类别创建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;
}
- 通过Executor执行Sql操作(这里以SimpleExecutor为例)
SqlSession的update/insert/delete操作会调用BaseExecutor的update方法
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(); //先清局部缓存,再更新,如何更新由子类实现,模板方法模式
return doUpdate(ms, parameter); //由子类实现
}
下面看下子类SimpleExecutor的doUpdate方法
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//获得statementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = handler.prepare(getConnection(ms.getStatementLog())); //获取Statement对象
handler.parameterize(stmt);//设置参数
return handler.update(stmt); //最终是一个statement进行处理
} finally {
closeStatement(stmt);
}
}
- 继续看StatementHandler接口对象的创建过程
StatementHandler对象是通过Configuration的newStatementHandler方法创建的
//创建Statement对象(**会调用过滤器**)
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);
// 这里会调用Mybatis的所有插件,返回代理对象(责任链模式)
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
Configuration中创建的是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子类,所有RoutingStatementHandler的操作都会调用delegate对象来调用(<b>静态代理模式</b>)。
- 以PreparedStatementHandler为例,继续看SimpleExecutor.doUpdate方法调用的实现
先看StatementHandler.prepare()方法;这个方法会调用PreparedStatementHandler的父类BaseStatementHandler的prepare方法,父类方法会调用子类instantiateStatement的实现方法创建Statement对象,然后对生成的Statement对象做必要的设置
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);// 子类实现方法获取到Statument对象
setStatementTimeout(statement); // 配置的设置
setFetchSize(statement);
return statement;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
继续看子类(PreparedStatementHandler)方法instantiateStatement怎么创建Statement对象
// 看这里的代码就知道了,就是java JDBC的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql(); //获取执行的sql
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() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
以上就是通过PreparedStatementHandler对象获取到了JDBC的Statement对象,那拿到Statement对象之后,按照JDBC的流程肯定就是设置sql执行参数,然后执行。 我们回到SimpleExecutor.doUpdate方法,在获取到Statement对象之后,调用了StatementHandler的parameterize来设置对应的参数
public void parameterize(Statement statement) throws SQLException {
//这个方法就一句代码,调用parameterHandler.setParameters方法实现
parameterHandler.setParameters((PreparedStatement) statement);
}
我们继续看下ParameterHandler的唯一实现类DefaultParameterHandler,看下具体的setParameters方法实现
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null){
jdbcType = configuration.getJdbcTypeForNull();
}
//ps.setXXX();设置参数值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
到此我们获取到了Statement对象,执行的参数值也设置好了,最后只要调用Statement的update方法即可执行相应的sql语句。看PreparedStatementHandler的update方法实现,很简单,返回sql执行受影响的行数,如果有自增列则处理
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //很简单,调用JDBC代码
int rows = ps.getUpdateCount(); //获取sql执行受影响的行数
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
- 到此我们SqlSession对象的insert/update/delete的操作调用过程结束。
小结
在SqlSession接口调用的insert/update/delete方法中,所有的操作都交给了Executor来操作。SqlSession接口是Mybatis框架暴露的外部接口,而Executor是内部的实现接口。在Executor的实现中,又是调用StatementHandler来处理的。当然,在调用StatementHandler设置参数时候,需要ParameterHandler来设置相应的参数,具体如下图:
当然,这里我们分析的是sql的insert/update/delete,没有分析select。所以没有涉及到ResultSetHandler接口对结果集处理(后面文章继续)。
Mybatis四大接口对象(本篇涉及到三个):Executor StatementHandler ParameterHandler ResultSetHander