Mybatis 源碼下載地址:https://github.com/mybatis/mybatis-3
Mybatis官方文檔地址:https://mybatis.org/mybatis-3/index.html
1,Mybatis的整體架構
2,Mybatis源碼思維導圖(畫質壓縮了,可私信要思維導圖)
3,mybatis三大核心流程:
4,Mybatis的初始化
(1)先閱讀官方問文檔
先導入mybatis的maven依賴
每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲核心的。SqlSessionFactory 的實例可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置文件或一個預先定製的 Configuration 的實例構建出 SqlSessionFactory 的實例。
從 XML 文件中構建 SqlSessionFactory 的實例非常簡單,建議使用類路徑下的資源文件進行配置。 但是也可以使用任意的輸入流(InputStream)實例,包括字符串形式的文件路徑或者 file:// 的 URL 形式的文件路徑來配置。MyBatis 包含一個名叫 Resources 的工具類,它包含一些實用方法,可使從 classpath 或其他位置加載資源文件更加容易
XML 配置文件中包含了對 MyBatis 系統的核心設置,包含獲取數據庫連接實例的數據源(DataSource)和決定事務作用域和控制方式的事務管理器(TransactionManager)
(2)寫好測試demo,進入斷點調試
@Test
public void test1() throws IOException {
// 讀取mybatis的配置文件
InputStream in = Resources.getResourceAsStream("config/mybatisconfig.xml");
// 創建SqlSessionFactory對象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
// 獲取session
SqlSession sqlSession = sqlSessionFactory.openSession();
// 動態代理模式獲取mapper實例
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
String name = mapper.getUserNameById(1);
System.out.println("test1==============>"+name);
}
(3)初始化階段
首先進入SqlSessionFactoryBuilder.class
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 初始化XMLConfigBuilder對象,讀取mybatisconfig.xml裏面的配置
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
進入parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("*[local-name()='configuration']"));
return configuration;
}
進入parseConfiguration(parser.evalNode("*[local-name()=‘configuration’]")),括號裏的把標籤就是mybatisconfig.xml配置文件裏面的標籤
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("*[local-name()='properties']"));
Properties settings = settingsAsProperties(root.evalNode("*[local-name()='settings']"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("*[local-name()='typeAliases']"));
pluginElement(root.evalNode("*[local-name()='plugins']"));
objectFactoryElement(root.evalNode("*[local-name()='objectFactory']"));
objectWrapperFactoryElement(root.evalNode("*[local-name()='objectWrapperFactory']"));
reflectorFactoryElement(root.evalNode("*[local-name()='reflectorFactory']"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("*[local-name()='environments']"));
databaseIdProviderElement(root.evalNode("*[local-name()='databaseIdProvider']"));
typeHandlerElement(root.evalNode("*[local-name()='typeHandlers']"));
// 解析<mappers>節點
mapperElement(root.evalNode("*[local-name()='mappers']"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
上述源碼裏面的properties,settings等是解析mybatisconfig.xml配置文件裏面的標籤,如下圖:
再進入,propertiesElement(root.evalNode("*[local-name()=‘properties’]"));
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
// properties加載配置文件
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
// 根據url加載配置文件
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 講配置文件中的信息與configuration裏面的variables進行合併
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 更新解析器parser和configuration裏面的variables
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
然後step out這個方法,進入mapperElement(root.evalNode("*[local-name()=‘mappers’]")),XMLMapperBuilder粉墨登場,用於解析mapper.xml
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 獲取<mappers>子節點
for (XNode child : parent.getChildren()) {
// 是否有<package>子節點
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 獲取resource,url,class三個互斥子節點
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// 如果resource子節點不爲空
if (resource != null && url == null && mapperClass == null) {
// 加載mapper映射文件
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 創建XMLMapperBuilder對象,用於解析mapper的映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
// 如果url子節點不爲空
} else if (resource == null && url != null && mapperClass == null) {
// // 加載mapper映射文件
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
// 創建XMLMapperBuilder對象,用於解析mapper的映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
// 如果class子節點不爲空
} else if (resource == null && url == null && mapperClass != null) {
// 加載class對象
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 向代理中心註冊mapper
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
mapperParser.parse();進入mapper解析器
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("*[local-name()='mapper']"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
進入configurationElement(parser.evalNode("*[local-name()=‘mapper’]")),開始解析mapper.xml文件,解析的是mapper.xml的節點而不是每一個sql裏面的節點
private void configurationElement(XNode context) {
try {
// 獲取命名空間namespace屬性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 設置命名空間namespace屬性
builderAssistant.setCurrentNamespace(namespace);
// 解析cache-ref節點
cacheRefElement(context.evalNode("*[local-name()='cache-ref']"));
cacheElement(context.evalNode("*[local-name()='cache']"));
// 解析parameterMap節點
parameterMapElement(context.evalNodes("*[local-name()='parameterMap']"));
// 解析 resultMap節點
resultMapElements(context.evalNodes("*[local-name()='resultMap']"));
sqlElement(context.evalNodes("*[local-name()='sql']"));
// 解析select,update,insert,delete節點
buildStatementFromContext(context.evalNodes("*[local-name()='select' or local-name()='insert' or local-name()='update' or local-name()='delete']"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
點擊進入buildStatementFromContext(context.evalNodes("*[local-name()=‘select’ or local-name()=‘insert’ or local-name()=‘update’ or local-name()=‘delete’]")),重點分析解析select,update,insert,delete節點
// 解析select,update,insert,delete節點
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
// 解析所以sql語句節點 ,並註冊到configuration中
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 創建XMLStatementBuilder對象,專門用於解析sql語句節點
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析sql語句節點
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
進入解析sql語句節點方法statementParser.parseStatementNode();
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 用於解析對應每一個sql節點裏面的標籤屬性
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
至此mybatis初始化工作完成,配置文件的所有信息都被加載到了sqlSessionFactory中的configration對象內
(3)代理階段
如UserMapper mapper = sqlSession.getMapper(UserMapper.class),進入getMapper()
@Override
public <T> T getMapper(Class<T> type) {
/*使用configuration對象得到mapper對象,
而configuration對象以初始化在內存中,
單例模式,生命週期貫穿整個應用週期*/
return configuration.<T>getMapper(type, this);
}
進入getMapper()方法裏面
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 使用動態代理的方式實現mapper接口,直接到invoke()反射內查看代碼
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 一個sqlSession對應一個接口方法
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
點擊進入mapperProxyFactory.newInstance(sqlSession)
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
MapperProxy創建對象,invoke()反射法動態代理生成mapper對象
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
// 記錄關聯的sqlSession對象
private final SqlSession sqlSession;
// mapper接口對應的class對象
private final Class<T> mapperInterface;
// key是mapper接口中某個方法的method對象,value對應的是MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 從緩存中獲取一個MapperMethod對象,如果緩存中沒有,就創建一個添加到緩存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//調用excute方法執行sql
return mapperMethod.execute(sqlSession, args);
}
進入執行sql語句的方法mapperMethod.execute(sqlSession, args)
/**
* @author Clinton Begin
* @author Eduardo Macarron
* @author Lasse Voss
*/
public class MapperMethod {
// 從configuration中獲取方法的命名空間,方法名以及sql語句的類型
private final SqlCommand command;
// 封裝mapper接口方法的相關信息(入參。返回類型)
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 通過sql的類型,接口返回參數,選擇對應的INSERT...
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:
// 查看method對象對返回結果類型的判斷
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
// 返回集合或數組
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
// 返回map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
// 返回遊標
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 返回單一對象的情況,通過參數解析器解析
Object param = method.convertArgsToSqlCommandParam(args);
// 查看select返回單個對象的底層實現
result = sqlSession.selectOne(command.getName(), param);
}
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;
}
進入new MethodSignature(config, mapperInterface, method),獲取封裝mapper的相關參數
public static class MethodSignature {
// 返回相關參數類型的布爾值定義
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
// 該方法參數解析器
private final ParamNameResolver paramNameResolver;
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 通過解析器解析該方法返回參數的類型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
// 初始化返回值字段
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.returnsCursor = Cursor.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
再進入selectOne()方法查看底層實現
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<? extends V> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<V>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
由此可知不管是返回的單個對象,還是map,或是其他的,底層都是通過selectList()方法,返回list集合,最後做的轉換
sqlSession查詢接口嵌套關係:
(4)數據讀寫階段
Executor的三個重要組件
- StatementHandler:它的作用是使用數據庫的Statement或PrepareStatement執行操作,啓承上啓下作用;
- ParameterHandler:對預編譯的SQL語句進行參數設置
- ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型;
接着上一段源碼,進入selelectList()方法
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 從configuration中獲取要執行sql語句執行信息
MappedStatement ms = configuration.getMappedStatement(statement);
// 此處進入數據庫階段,通過executor執行sql語句,並返回指定結果集
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();
}
}
再進入query()方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 獲取sql語句信息,包括佔位符,參數等信息
BoundSql boundSql = ms.getBoundSql(parameter);
// 拼裝緩存的key值
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
// 檢查exector是否關閉
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 非嵌套查詢並且FlushCache配置爲true,則要清空一級緩存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 查詢層次加1
queryStack++;
// 查詢以及緩存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// 針對存儲過程的結果處理
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 緩存未命中,從數據庫加載數據,數據庫連接開始
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
// 延遲加載處理
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
進入真正訪問數據庫的方法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 {
// 調用doQuery()方法並接收返回結果
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;
}
進入查詢的實現方法doQuery(),模板設計模式由子類實現3個方法,批量數據庫操作,緩存預編譯處理,默認處理器 (依次對應)
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 獲取configuration對象
Configuration configuration = ms.getConfiguration();
// StatementHandler創建對象handler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 使用prepareStatement()預編譯對佔位符進行處理
stmt = prepareStatement(handler, ms.getStatementLog());
// 通過StatmentHandler調用ResultSetHandler將結果集轉化爲指定對象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
進入prepareStatement()預編譯處理
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
// 創建Statement
Statement stmt;
// 通過動態代理獲取connect對象,並添加日誌功能
Connection connection = getConnection(statementLog);
// 通過不同的StatementHandler,利用connect創建Statement
stmt = handler.prepare(connection, transaction.getTimeout());
// 使用parameterize()處理佔位符
handler.parameterize(stmt);
return stmt;
}
parameterHandlder主鍵以及飢渴難耐,進入parameterize()處理佔位符
@Override
public void parameterize(Statement statement) throws SQLException {
// parameterHandler主鍵開始進行佔位符處理
parameterHandler.setParameters((PreparedStatement) statement);
}
進入setParameters(),進入佔位符處理的具體業務
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 從boundSql中獲取sql語句佔位符參數信息
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)) { // issue #448 ask first for additional params
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);
}
// 從parameterMapping中獲取typeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 獲取對應參數的jdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
佔位符處理完畢開始執行sql,進入handler.query(stmt, resultHandler)
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 似曾相識的jdbc操作
PreparedStatement ps = (PreparedStatement) statement;
// 執行sql語句
ps.execute();
// resultSetHandler對sql結果進行處理
return resultSetHandler.<E> handleResultSets(ps);
}
進入resultSetHandler. handleResultSets(ps),查看sql返回結果處理
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// 保存結果集的對象
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// statement肯返回多個結果集的對象,這裏先取第一個
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 獲取結果集resultMap,本質就是獲取字段與java屬性的映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 獲取結果集不能爲空,爲空拋出異常
while (rsw != null && resultMapCount > resultSetCount) {
// 獲取當前結果集對應的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
// 根據映射規則(resultMap)對結果集進行轉化,並放入multipleResults
handleResultSet(rsw, resultMap, multipleResults, null);
// 獲取下一個結果集
rsw = getNextResultSet(stmt);
// 清空nestedResuletsetObject對象
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//獲取多結果集,多結果集多存在與存儲過程,返回多個resultSets,下次更新
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
Excutor內幕結構流程:
最後接上面測試demo運行結果:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 2104545713.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7d70d1b1]
==> Preparing: select name from t_user where id=?
==> Parameters: 1(Integer)
<== Columns: name
<== Row: 張三
<== Total: 1
test1==============>張三