第一節 參考
一.參考資料
https://github.com/mybatis/mybatis-3
mybatis的單元測試用的hsqldb資料庫,需要自己建個單元測試用例, 連接配接mysql.我的測試用例如下圖所示.
第二節 架構
一.分層
核心原理三步走:
1.啟動讀取xml配置,解析到Configuration類中,以Configuration為構造方法參數建立SqlSessionFactory
2.通過DefaultSqlSessionFactory類建立DefaultSqlSession,通過DefaultSqlSession擷取到mapper接口的代理類.
3.通過mapper接口的代理類執行sql語句
具體過程:
Configuration+代理+指令模式+委托模式。
所有的mybatis配置讀取xml到Configuration類中. 然後調用MapperProxyFactory類,生成所有mapper接口類的代理類MapperProxy.在MapperProxy類的invoke方法中,調用MapperMethod.execute方法中執行各種指令.具體指令由RoutingStatementHandler委托具體的類處理,執行由STATEMENT,PREPARED,CALLABLE類型的具體類型處理.
二.SqlSessionFactoryBuilder/SqlSessionFactory
三.SqlSession
四.Configuration
五.Executor
六.BoundSql
七.StatementHandler
八.ResultSetHandler
第三節 源碼細節
一.啟動讀取xml配置,建立SqlSessionFactory
調用SqlSessionFactoryBuilder#build(Reader)使用builder模式建立建立SqlSessionFactory.入參是是xml檔案生成的Reader.主要是填充Configuration類對象,然後把這個對象作為DefaultSqlSessionFactory構造方法的參數.
xml檔案使用sax庫解析到XMLConfigBuilder類中.然後進入XMLConfigBuilder#parseConfiguration(XNode)解析到Configuration類型的BaseBuilder#configuration成員中.XMLConfigBuilder類繼承自BaseBuilder類,BaseBuilder類中成員如下:
public abstract class BaseBuilder {
//xml中的各種配置
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
}
1.解析xml内容的方法XMLConfigBuilder#parseConfiguration()代碼如下:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
/* 解析properties标簽中的name value值存儲到Properties類型的Configuration#variables成員中
比如示例代碼中的<property name="dialect" value="mysql" /> */
propertiesElement(root.evalNode("properties"));
/* 解析setting标簽為Properties格式,比如示例中的<setting name="mapUnderscoreToCamelCase" value="true" /> */
Properties settings = settingsAsProperties(root.evalNode("settings"));
//解析VFS的子類放到Configuration#vfsImpl成員中,沒用過
loadCustomVfs(settings);
//解析Log接口的實作類放到Configuration#logImpl成員中,沒用過
loadCustomLogImpl(settings);
/* 解析typeAliases,調用TypeAliasRegistry#registerAliases()注冊類型别名到TypeAliasRegistry#TYPE_ALIASES成員Map中*/
typeAliasesElement(root.evalNode("typeAliases"));
/*注冊plugins插件到類型為InterceptorChain的Configuration#interceptorChain中*/
pluginElement(root.evalNode("plugins"));
/*解析objectFactory到類型為ObjectFactory的Configuration#objectFactory成員中*/
objectFactoryElement(root.evalNode("objectFactory"));
/*解析objectWrapperFactory到類型為ObjectWrapperFactory的成員Configuration#objectWrapperFactory中*/
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
/*解析reflectorFactory标簽到類型為ReflectorFactory的成員Configuration#reflectorFactory中*/
reflectorFactoryElement(root.evalNode("reflectorFactory"));
/*解析setting标簽到configuration成員中,具體支援的設定項入下面代碼2處所示.*/
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
/*解析environments标簽到類型為Environment的成員Configuration#environment中這裡面存放了jdbc連接配接的配置,Environment類成員在下面代碼給出*/
environmentsElement(root.evalNode("environments"));
/*解析databaseIdProvider标簽的值到Configuration#databaseId*/
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
/*解析typeHandlers到TypeHandlerRegistry#TYPE_HANDLER_MAP的成員map中*/
typeHandlerElement(root.evalNode("typeHandlers"));
/*注冊所有xml的mapper檔案,内部邏輯是針對每個xml檔案,建立XMLMapperBuilder對象,調用XMLMapperBuilder#parse()解析mapper檔案,代碼在後面3處分析*/
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
Environment類成員:
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
}
2.mybatis支援的setting配置項
進入XMLConfigBuilder#settingsElement()方法,代碼如下:
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
3.解析mapper的xml檔案
進入XMLMapperBuilder#parse(),代碼如下:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
/*代碼在下面分析*/
configurationElement(parser.evalNode("/mapper"));
//把目前的mapper檔案加到Configuration#loadedResources的set中,代表已經加載過
configuration.addLoadedResource(resource);
/* 把目前解析的mapper對應的class加到MapperRegistry#knownMappers的成員中,key是Class對象,value是MapperProxyFactory類型,這個MapperProxyFactory是直接new出來的泛型類對象,泛型指向mapper檔案對應的Class接口類型,比如xxx.UserMapper.然後調用MapperAnnotationBuilder#parse()方法解析Mapper檔案對應的Class接口中的每個方法上面的注解。這種在接口方法上面通過注解寫sql語句的方法比較少用,不友善.*/
bindMapperForNamespace();
}
//解析之前不完整的ResultMap标簽,示例代碼沒有
parsePendingResultMaps();
//解析之前不完整的cacheref标簽,示例代碼沒有
parsePendingCacheRefs();
//解析之前不完整的statement語句,示例代碼沒有
parsePendingStatements();
}
解析mapper檔案标簽
private void configurationElement(XNode context) {
try {
//擷取namespace,比如com.xx.UserMapper接口,成員方法對應xml的每個sql語句
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
//解析cache-ref标簽到Configuration#cacheRefMap中
cacheRefElement(context.evalNode("cache-ref"));
//解析cache緩存标簽到Configuration#caches成員中.
cacheElement(context.evalNode("cache"));
//解析parameterMap标簽到Configuration#parameterMaps成員中.
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
/*resultMap代表damain對象中字段,類型和資料庫列的一一映射關系.解析到Configuration#resultMaps成員map中,key是id,value是個ResultMap類.這個類的成員在下面4給出.*/
resultMapElements(context.evalNodes("/mapper/resultMap"));
/*周遊所有的sql片段語句到XMLMapperBuilder#sqlFragments的map中.key是sql的id.value是XNode格式.*/
sqlElement(context.evalNodes("/mapper/sql"));
/*周遊所有的sql語句,解析到Configuration#mappedStatements的map中,key是id,value是MappedStatement類,代表一個sql語句,它的類成員在後面4給出.*/
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
4.存儲xml标簽和sql語句的一些資料類.
ResultMap類,代表domain對象和資料庫表的列的映射關系,代碼如下:
public class ResultMap {
private Configuration configuration;
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
private List<ResultMapping> idResultMappings;
private List<ResultMapping> constructorResultMappings;
private List<ResultMapping> propertyResultMappings;
private Set<String> mappedColumns;
private Set<String> mappedProperties;
private Discriminator discriminator;
private boolean hasNestedResultMaps;
private boolean hasNestedQueries;
private Boolean autoMapping;
}
代表一個mapper中select|insert|update|delete的sql語句的類.代碼如下:
public final class MappedStatement {
//xml檔案的全路徑
private String resource;
private Configuration configuration;
//sql語句的全路徑,比如com.xxx.UserMapper.selectByExample
private String id;
private Integer fetchSize;
private Integer timeout;
/*語句類型,枚舉值enum StatementType {
STATEMENT, PREPARED, CALLABLE
}, 預設是PREPARED枚舉*/
private StatementType statementType;
//預設是Default枚舉
private ResultSetType resultSetType;
//原始的sql語句,預設是DynamicSqlSource類型,内有SqlNode類型的rootSqlNode成員存儲sql語句.在後面給出SqlNode類成員.
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
/*sql指令類型,枚舉值 enum SqlCommandType {
UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}
*/
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
}
存儲Sql語句的遞歸結構
public class MixedSqlNode implements SqlNode {
private final List<SqlNode> contents;
}
二.通過DefaultSqlSessionFactory類擷取到mapper接口的代理類.
1.DefaultSqlSessionFactory#openSession()擷取SqlSession對象.進入DefaultSqlSessionFactory#openSessionFromDataSource()方法建立SqlSession.代碼如下:
參數ExecutorType是枚舉類型,預設是SIMPLE.
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
參數TransactionIsolationLevel隔離級别也是枚舉.
public enum TransactionIsolationLevel {
NONE(Connection.TRANSACTION_NONE),
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//擷取xml檔案中配置的jdbc連接配接參數.
final Environment environment = configuration.getEnvironment();
//擷取事務工廠為JdbcTransactionFactory類
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//建立一個Transaction事務對象,jdbc的為JdbcTransaction.類成員在後面2給出
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//預設建立SimpleExecutor執行器.作為CachingExecutor緩存執行器的成員,委托模式.
//如果有Interceptor,在這裡InterceptorChain#interceptors成員添加攔截這個CachingExecutor執行器.
final Executor executor = configuration.newExecutor(tx, execType);
//建立DefaultSqlSession對象傳回,DefaultSqlSession成員在後面2給出
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();
}
}
2.Jdbc事務對象
public class JdbcTransaction implements Transaction {
//事務所在連接配接
protected Connection connection;
//資料源,連接配接的資料庫位址,密碼等
protected DataSource dataSource;
//事務隔離級别
protected TransactionIsolationLevel level;
//預設為false,不自動送出
protected boolean autoCommit;
}
預設的Sql會話類
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
//執行器,預設CachingExecutor中引用SimpleExecutor.
private final Executor executor;
//預設為false
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
}
3.通過DefaultSqlSession擷取mapper接口的代理類
進入DefaultSqlSession#getMapper(),參數為xxx.UserMapper的接口Class.實際調用成員Configuration#mapperRegistry的getMapper方法,mapperRegistry的填充在前面一.3中分析了.MapperRegistry#knownMappers的key是Class對象,value是MapperProxyFactory類型,這個MapperProxyFactory是直接new出來的泛型類對象,泛型指向mapper檔案對應的Class接口類型,比如xxx.UserMapper代碼如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//擷取MapperProxyFactory泛型類.
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//建立mapper接口的代理對象,代碼如下所示
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public T newInstance(SqlSession sqlSession) {
//MapperProxy實作了InvocationHandler接口,是代理類,所有這個mapper的sql語句都調用它的invoke方法
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
//代碼在下面,調用Proxy.newProxyInstance建立代理類,代理類調用MapperProxy類.
return newInstance(mapperProxy);
}
建立動态代理
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
mapper接口代理類
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
//代理的mapper接口
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
//sql語句代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//代理類的正常想法,去掉Object的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
//如果是靜态,abstract方法,調用原對象
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/*取出緩存MapperProxy#methodCache中的MapperMethod對象,開始是空的.建立MapperMethod對象後放進去,MapperMethod類成員下面給出*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
//執行sql語句
return mapperMethod.execute(sqlSession, args);
}
}
public class MapperMethod {
//方法對應的sql指令,包含方法名全路徑,方法類型
private final SqlCommand command;
//方法簽名,在下面給出類成員
private final MethodSignature method;
}
方法簽名類
public static class MethodSignature {
//是否傳回多條記錄
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
//傳回類型
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;
}
三.通過mapper接口的代理類執行sql語句.
1.比如執行Employee employee = employeeMapper.selectByPrimaryKey(id)語句.
通過上面二.3的最後分析,進入MapperProxy#invoke()方法,調用MapperMethod#execute().通過指令模式實作,代碼如下:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
//根據select的傳回多條記錄,一條記錄,還是map等調用不同方法.
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//上面的selectByPrimaryKey(id)測試代碼進入這裡.
//把傳進來的sql語句的參數轉成Map<String, Object>類型的param
Object param = method.convertArgsToSqlCommandParam(args);
//執行sql查詢,測試代碼進入DefaultSqlSession#selectOne()方法,代碼在後面2處分析,政策模式
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
2.select類型的sql查詢
如果隻傳回一條記錄,進入DefaultSqlSession#selectOne().調用DefaultSqlSession#selectList(),代碼如下:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/* 取出xml填充到Configuration#mappedStatements中的sql語句.這個填充過程在前面一.3處分析過了.MappedStatement類成員代碼在前面的一.4中有。具體的sql語句在MappedStatement#sqlSource成員中遞歸存儲*/
MappedStatement ms = configuration.getMappedStatement(statement);
//調用CachingExecutor#query(),代碼在下面分析
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
緩存執行器執行查詢CachingExecutor#query(),代碼如下:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//建立動态sql
BoundSql boundSql = ms.getBoundSql(parameterObject);
//緩存sql語句,key為為xml中的sql語句id+offset+limit+sql語句
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
//調用代碼在下面
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
動态sql類BoundSql成員,裡面有?,#{id}等占位符
public class BoundSql {
//存放字元串形式的具體的sql語句
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
}
緩存執行器執行查詢CachingExecutor#query(),代碼如下:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//如果有緩存的結果,從緩存中取結果
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
/* 沒有緩存,調用BaseExecutor#query()方法執行,進入BaseExecutor#queryFromDatabase(),代碼在下面分析*/
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor#queryFromDatabase()執行查詢,代碼如下:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//調用SimpleExecutor#doQuery()執行查詢.在後面的3處分析
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
//查詢完把查詢結果更新到緩存
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
3. SimpleExecutor#doQuery()執行查詢.代碼如下:
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();
/* 建立RoutingStatementHandler執行sql.加入StatementHandler的目的是用來區分StatementType枚舉的三種調用STATEMENT, PREPARED, CALLABLE.如下面的RoutingStatementHandler類的構造方法所示,每種枚舉對應不同的StatementHandler*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//調用SimpleExecutor#prepareStatement()準備sql語句,準備過程在下面分析
stmt = prepareStatement(handler, ms.getStatementLog());
//調用jdbc執行sql查詢,代碼在後面分析
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
RoutingStatementHandler建立不同的種類的StatementHandler.内部成員delegate委托模式具體執行sql語句.
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());
}
}
準備sql語句,進入SimpleExecutor#prepareStatement()
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//調用JdbcTransaction#openConnection(),進入javax.sql.DataSource#getConnection()擷取資料庫連接配接
Connection connection = getConnection(statementLog);
//處理sql語句的占位符
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
調用jdbc執行sql查詢,向mysql通過socket發出sql語句.進入SimpleStatementHandler#query,如下代碼所示:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
//調用jdbc的方法java.sql.Statement#execute(java.lang.String)執行sql語句
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}