記錄是一種精神塔逃,是加深理解最好的方式之一弃揽。
最近看了下Mybatis的源碼讹蘑,了解了下SqlSession執(zhí)行Sql的過程蓄诽,在這里把他記下來
曹金桂 cao_jingui@163.com(如有遺漏之處還請指教)
時間:2016年10月5日14:50
SqlSession的delete/update/insert執(zhí)行過程
調(diào)用過程說明
- 用戶代碼獲取到SqlSession對象后(DefaultSqlSession)绽慈,調(diào)動SqlSession的insert/update/delete
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement); //獲取MappedStatement對象恨旱,此對象包含了對應(yīng)Mapper的所有配置信息
return executor.update(ms, wrapCollection(parameter)); //調(diào)用Executor對象的update方法
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
SqlSession中的Executor對象在Configuration中創(chuàng)建的
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//確保ExecutorType不為空(defaultExecutorType有可能為空)
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 根據(jù)ExecutorType類別創(chuàng)建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); //調(diào)用插件攔截器
return executor;
}
- 通過Executor執(zhí)行Sql操作(這里以SimpleExecutor為例)
SqlSession的update/insert/delete操作會調(diào)用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(); //先清局部緩存,再更新坝疼,如何更新由子類實現(xiàn)搜贤,模板方法模式
return doUpdate(ms, parameter); //由子類實現(xiàn)
}
下面看下子類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);//設(shè)置參數(shù)
return handler.update(stmt); //最終是一個statement進(jìn)行處理
} finally {
closeStatement(stmt);
}
}
- 繼續(xù)看StatementHandler接口對象的創(chuàng)建過程
StatementHandler對象是通過Configuration的newStatementHandler方法創(chuàng)建的
//創(chuàng)建Statement對象(**會調(diào)用過濾器**)
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);
// 這里會調(diào)用Mybatis的所有插件,返回代理對象(責(zé)任鏈模式)
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
Configuration中創(chuàng)建的是RoutingStatementHandler對象钝凶,其實這個對象就是StatementHandler的代理對象(靜態(tài)代理)仪芒,創(chuàng)建過程如下:
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());
}
}
以上根據(jù)StatementType創(chuàng)建不同的StatementHandler子類,所有RoutingStatementHandler的操作都會調(diào)用delegate對象來調(diào)用(<b>靜態(tài)代理模式</b>)耕陷。
- 以PreparedStatementHandler為例掂名,繼續(xù)看SimpleExecutor.doUpdate方法調(diào)用的實現(xiàn)
先看StatementHandler.prepare()方法;這個方法會調(diào)用PreparedStatementHandler的父類BaseStatementHandler的prepare方法,父類方法會調(diào)用子類instantiateStatement的實現(xiàn)方法創(chuàng)建Statement對象哟沫,然后對生成的Statement對象做必要的設(shè)置
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);// 子類實現(xiàn)方法獲取到Statument對象
setStatementTimeout(statement); // 配置的設(shè)置
setFetchSize(statement);
return statement;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
繼續(xù)看子類(PreparedStatementHandler)方法instantiateStatement怎么創(chuàng)建Statement對象
// 看這里的代碼就知道了饺蔑,就是java JDBC的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql(); //獲取執(zhí)行的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對象之后嗜诀,按照J(rèn)DBC的流程肯定就是設(shè)置sql執(zhí)行參數(shù)猾警,然后執(zhí)行。 我們回到SimpleExecutor.doUpdate方法隆敢,在獲取到Statement對象之后发皿,調(diào)用了StatementHandler的parameterize來設(shè)置對應(yīng)的參數(shù)
public void parameterize(Statement statement) throws SQLException {
//這個方法就一句代碼,調(diào)用parameterHandler.setParameters方法實現(xiàn)
parameterHandler.setParameters((PreparedStatement) statement);
}
我們繼續(xù)看下ParameterHandler的唯一實現(xiàn)類DefaultParameterHandler筑公,看下具體的setParameters方法實現(xiàn)
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();設(shè)置參數(shù)值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
到此我們獲取到了Statement對象雳窟,執(zhí)行的參數(shù)值也設(shè)置好了,最后只要調(diào)用Statement的update方法即可執(zhí)行相應(yīng)的sql語句匣屡》饩龋看PreparedStatementHandler的update方法實現(xiàn),很簡單捣作,返回sql執(zhí)行受影響的行數(shù)誉结,如果有自增列則處理
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //很簡單,調(diào)用JDBC代碼
int rows = ps.getUpdateCount(); //獲取sql執(zhí)行受影響的行數(shù)
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
- 到此我們SqlSession對象的insert/update/delete的操作調(diào)用過程結(jié)束券躁。
小結(jié)
在SqlSession接口調(diào)用的insert/update/delete方法中惩坑,所有的操作都交給了Executor來操作掉盅。SqlSession接口是Mybatis框架暴露的外部接口,而Executor是內(nèi)部的實現(xiàn)接口以舒。在Executor的實現(xiàn)中趾痘,又是調(diào)用StatementHandler來處理的。當(dāng)然蔓钟,在調(diào)用StatementHandler設(shè)置參數(shù)時候永票,需要ParameterHandler來設(shè)置相應(yīng)的參數(shù),具體如下圖:
當(dāng)然滥沫,這里我們分析的是sql的insert/update/delete侣集,沒有分析select。所以沒有涉及到ResultSetHandler接口對結(jié)果集處理(后面文章繼續(xù))兰绣。
Mybatis四大接口對象(本篇涉及到三個):Executor StatementHandler ParameterHandler ResultSetHander