一起學習Mybatis----mybatis的執行原理
mybatis的生命週期包含4部分:
1. SqlSessionFactoryBuilder
2.SqlSessionFactory
3.SqlSession
4.Mapper
mybatis的執行是先根據相關的配置檔案通過SqlSessionFactoryBuilder構建SqlSessionFactory,在根據SqlSessionFactory去建立響應的SqlSession,SqlSession在根據Mapper傳遞的sql,進行相關的動作,返回結構給Mapper.
構建SqlSessionFactory過程:
第一步:
通過 org.apache. ibatis. builder.xmL.XMLConfigBuilder解析配置的XML檔案,讀出配置引數,並將讀取的資料存入這個org.apache ibatis session. Configuration類中。注意MyBatis幾乎所有的配置都是存在這裡的。
第二步:
使用 Confinguration物件去建立 SqlSession Factory。 My Batis中的 SqlSession Factory是一個介面,而不是實現類,為此 My Batis提供了一個預設的 Sqlsession Factory實現類,我們般都會使用它 org.apache ibatis session. defaults. Defaultsqlsession Factory。注意,在大部分情況下我們都沒有必要自己去建立新的 Sqlsession Factory的實現類。
Configuration 原始碼:
public class Configuration { protected Environment environment; protected boolean safeRowBoundsEnabled; protected boolean safeResultHandlerEnabled; protected boolean mapUnderscoreToCamelCase; protected boolean aggressiveLazyLoading; protected boolean multipleResultSetsEnabled; protected boolean useGeneratedKeys; protected boolean useColumnLabel; protected boolean cacheEnabled; protected boolean callSettersOnNulls; protected boolean useActualParamName; protected boolean returnInstanceForEmptyRow; protected String logPrefix; protected Class<? extends Log> logImpl; protected Class<? extends VFS> vfsImpl; protected LocalCacheScope localCacheScope; protected JdbcType jdbcTypeForNull; protected Set<String> lazyLoadTriggerMethods; protected Integer defaultStatementTimeout; protected Integer defaultFetchSize; protected ExecutorType defaultExecutorType; protected AutoMappingBehavior autoMappingBehavior; protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior; protected Properties variables; protected ReflectorFactory reflectorFactory; protected ObjectFactory objectFactory; protected ObjectWrapperFactory objectWrapperFactory; protected boolean lazyLoadingEnabled; protected ProxyFactory proxyFactory; 。。。。。。
Configuration作用:
1. 讀入配置檔案,包括基礎配置的XML檔案和對映器的XML檔案。
2.初始化基礎配置,比如 My Batis的別名等,一些重要的類物件,例如,外掛、對映器、 ObjectFactory和 type Handler物件
3.提供單例,為後續建立 SessionFactory服務並提供配置的引數執行一些重要的物件方法,初始化配置資訊。
在第一步中Mybatis會讀取所有的xml檔案,將其初始化儲存到 Configuration類的單例物件中,初始化包含了:properties全域性引數,settings設定,typeAliases別名,typeHandler型別處理器,ObjectFactory物件,plugin外掛,environment環境,DatabaseldProvider資料庫標識,Mapper對映器。
sqlSessionFactory=new SqlSessionFactoryBuilder(). build(inputstream).
org.apache.ibatis.session.SqlSessionFactoryBuilder中的builder方法。
MyBatis會根據 Configuration的配置讀取所配置的資訊,構建 SqlsessionFactory物件。
SqLSession執行過程:
探究其原理的瞭解一下 Mapper對映原理。
Mapper對映器:構建需要執行的Sql指令碼。由三部分組成:
1. MappedStatement,它儲存對映器的一個節點( select|insert|delete|update)。包括許多我們配置的SQL、SQL的id、快取資訊、 resultMap、 parameterType、 resultType、languageDriver等重要配置內容。
2.SqLSource,它是提供 BoundS物件的地方,它是 MappedStatement的一個屬性。
3. BoundSql,它是建立SQL和引數的地方。它有3個常用的屬性:SQL、 parameterObject ,parameterMappings。
Mapper對映是通過動態代理實現的。
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//動態代理對Mapper介面實行了代理模式,而代理的方法都放置到了MapperProxy類中了
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
package org.apache.ibatis.binding;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
/**
動態代理模式
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
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 {
//判斷它是否是一個類,顯然這裡 Mapper是一個介面不是類,所以判定失敗
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
上面運用了 invoke方法。一旦 mapper是一個代理物件,那麼它就會執行到 invoke方法裡面, invoke首先判斷它是否是一個類,顯然這裡 Mapper是一個介面不是類,所以判定失敗。那麼就會生成 MapperMethod物件,它是通過 cachedMapperMethod方法對其初始化的,然後執行 execute方法,把 sqlsession和當前執行的引數傳遞進去。
Mapper執行的過程是通過 Executor、 StatementHandler、Parameter Handler和 Resulthandler來完成資料庫操作和結果返回的。
Executor代表執行器,由它來排程 Statementhandler、 ParameterHandler、 Resulthandler等來執行對應的SQL。
StatementHandler的作用是使用資料庫的 Statement( PreparedStatement)執行操作,它是四大物件的核心,起到承上啟下的作用。
ParameterHandler用於SQL對引數的處理。
Resulthandler是進行最後資料集( Resultset)的封裝返回處理的。
Executor執行器:建立執行器首先根據Configuration獲取執行器型別。
在建立sqlSession就開始初始化:
生成指定的執行器。
public class SimpleExecutor extends BaseExecutor {
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
@Override
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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
。。。。。。。。。。。。。。。。。。。。。。。。。。
}
顯然 MyBatis根據 Configuration來構建 Statementhandler,然後使用 prepareStatement方法,對SQL編譯並對引數進行初始化,我們在看它的實現過程,它呼叫了 Statementhandler的prepare()進行了預編譯和基礎設定,然後通過 StatementHandler的 parameterize()來設定引數並執行, resulthandler再組裝査詢結果返回給呼叫者來完成一次查詢。
資料庫會話器 StatementHandler:專門處理資料庫會話的。
建立會話器:
RoutingStatementHandler不是我們真實的服務物件,它是通過適配模式找到對應的StatementHandler來執行的。在MyBatis中, StatementHandler和 Executor一樣分為三種SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler它所對應的是三種執行器。
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());
}
}
我們最常用的是PreparedStatementHandler:
預編譯
預編譯完成就需要開始設定引數。
ParameterHandler:
設定完引數就需要開始查詢了,進行結構封裝。
Resulthandler:
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.resultset;
import org.apache.ibatis.cursor.Cursor;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @author Clinton Begin
*/
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
實現:
結果封裝返回。
SqlSession執行原理圖: