mybatis代理對(duì)象的創(chuàng)建過(guò)程
在上一遍 mybatis源碼分析(二):mybatis在執(zhí)行SQL語(yǔ)句之前都做了什么 中我們通過(guò)源碼分析看到mybatis是如何構(gòu)建SqlSessionFactory和SqlSession的瘾敢。其中我們看到SqlSession在構(gòu)建的過(guò)程中構(gòu)建了一個(gè)Executor,在Executor里包含了大量的數(shù)據(jù)庫(kù)操作,于是猜測(cè)Executor就是mybatis執(zhí)行SQL語(yǔ)句的核心組件。
這篇文章我們將繼續(xù)深入mybatis源碼來(lái)研究一下mybatis是如何執(zhí)行一條sql語(yǔ)句的戴已,同時(shí)mybatis又是如何處理輸入?yún)?shù)和輸出結(jié)果的固额。
首先我們寫(xiě)一段測(cè)試代碼:
執(zhí)行sql語(yǔ)句的mapper接口:
public interface DistrictMapper {
District getById(Integer id);
}
mapper對(duì)應(yīng)的sql xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.binf.mybatistudy.dao.DistrictMapper">
<select id="getById" resultType="me.binf.mybatistudy.entity.District">
select * from district where id = #{id}
</select>
</mapper>
測(cè)試用例:
@Test
public void testMyBatis() throws IOException {
//1头谜、得到 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2狐蜕、得到 sqlSession ,代表和數(shù)據(jù)庫(kù)一次回話(huà)
SqlSession sqlSession = sqlSessionFactory.openSession();
//3宠纯、得到真正操作數(shù)據(jù)庫(kù)的Mapper
DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);
//4.執(zhí)行方法方法并得到結(jié)果
District district = mapper.getById(1);
System.out.println(district);
//5.關(guān)閉sqlSession
sqlSession.close();
}
上面三段代碼我們上一篇文章已經(jīng)分析過(guò)了,今天我們主要是Debug這兩段代碼:
//3层释、得到真正操作數(shù)據(jù)庫(kù)的Mapper
DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);
//4.執(zhí)行方法方法并得到結(jié)果
District district = mapper.getById(1);
要知道DistrictMapper是一個(gè)接口婆瓜,接口是沒(méi)辦法執(zhí)行的。
DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);
那么這段代碼一定是mybatis為我們生成了一個(gè)代理對(duì)象贡羔,我們進(jìn)去看一下廉白。
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
......
}
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
......
}
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
......
}
我把mybatis生成代理對(duì)象核心的三個(gè)方法摘錄下來(lái)了,可以看到mybatis并沒(méi)有直接生成代理對(duì)象乖寒,而是使用MapperProxyFactory來(lái)生成DistrictMapper的代理對(duì)象猴蹂。而MapperProxyFactory是mybatis在構(gòu)建SqlSessionFactory的時(shí)候就已經(jīng)創(chuàng)建好,并且放入到MapperRegistry這個(gè)類(lèi)的knownMappers屬性Map中的楣嘁。
那么我們可以再來(lái)回溯一下MapperProxyFactory的創(chuàng)建過(guò)程:
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//用xml的namespace構(gòu)建mapper
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null && !configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
......
}
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
......
}
可以看到mybatis創(chuàng)建MapperProxyFactory過(guò)程是:
- 加載mybatis配置文件對(duì)應(yīng)的所有mapper.xml路徑
- 解析mapper.xml里所有的namespace標(biāo)簽磅轻,根據(jù)標(biāo)簽生成對(duì)應(yīng)接口的Class對(duì)象
- 把Class對(duì)象作為key,創(chuàng)建MapperProxyFactory對(duì)象作為value逐虚,然后放進(jìn)knownMappers里面
同時(shí)我們還可以看到mybatis在初始化階段不光解析了mapper.xml里namespace標(biāo)簽聋溜,還解析了ResultMaps、CacheRefs叭爱、Statements具體有什么左右我們后面再研究撮躁。可以看到mybatis這么設(shè)計(jì)的好處是可以在項(xiàng)目啟動(dòng)階段就可以幫你檢查出xml和接口對(duì)應(yīng)關(guān)系是否正確,同時(shí)提前把MapperProxyFactory構(gòu)建好緩存起來(lái)還可以加快后面程序運(yùn)行的效率买雾。
mybatis是如何為執(zhí)行SQL語(yǔ)句做準(zhǔn)備的
MapperProxyFactory對(duì)象已經(jīng)有了把曼,那么接下來(lái)就是mybatis如何執(zhí)行SQL語(yǔ)句了。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
......
}
動(dòng)態(tài)代理newProxyInstance核心參數(shù)是InvocationHandler漓穿,而MapperProxy就是mybatis為InvocationHandler提供的實(shí)現(xiàn)類(lèi)祝迂。我們主要去看MapperProxy類(lèi)的invoke()方法,這個(gè)方法就是mybatis執(zhí)行sql語(yǔ)句的入口器净。
public class MapperProxy<T> implements InvocationHandler, Serializable {
@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 {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
......
}
可以看到mybatis緩存很多,調(diào)用invoke方法的時(shí)候mybatis會(huì)把目標(biāo)方法對(duì)象給緩存起來(lái)当凡,每一個(gè)目標(biāo)方法對(duì)應(yīng)一個(gè)私有靜態(tài)類(lèi)PlainMethodInvoker再去執(zhí)行山害。
public class MapperMethod {
private final SqlCommand command;
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;
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:
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 {
Object param = method.convertArgsToSqlCommandParam(args);
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;
}
......
}
PlainMethodInvoker再去調(diào)用MapperMethod的execute方法去執(zhí)行SQL語(yǔ)句,mybatis的MapperMethod方法相當(dāng)于整個(gè)框架的分揀車(chē)間的作用沿量,分揀過(guò)程大概做了這幾件事:
1.MapperMethod實(shí)例化方法會(huì)把目標(biāo)方法的所有信息包裝成一個(gè)SqlCommand對(duì)象
2.根據(jù)目標(biāo)方法執(zhí)行的SQL類(lèi)型做判斷浪慌,通過(guò)不同類(lèi)型去執(zhí)行對(duì)應(yīng)的SQL邏輯
3.根據(jù)查詢(xún)方法的返回值類(lèi)型去調(diào)用不同查詢(xún)方法從而可以把查詢(xún)結(jié)果包裝成目標(biāo)方法定義的返回類(lèi)型
4.根據(jù)入?yún)⒌念?lèi)型和個(gè)數(shù)把入?yún)b成框架方便解析的包裝類(lèi)型
這里可以看到mybatis的一個(gè)核心組件:ParamNameResolver參數(shù)解析器。
public class ParamNameResolver {
public static final String GENERIC_NAME_PREFIX = "param";
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
if (object instanceof Collection) {
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
return object;
}
}
解析參數(shù)的邏輯并不復(fù)雜朴则,mybatis會(huì)判斷你定義的目標(biāo)方法參數(shù)的個(gè)數(shù)和類(lèi)型:
1.如果只有一個(gè)參數(shù)权纤,并且參數(shù)不是集合類(lèi)型或者是數(shù)組類(lèi)型就直接返回參數(shù)值
2.如果只有一個(gè)參數(shù),但是參數(shù)的類(lèi)型是集合類(lèi)型或者數(shù)組類(lèi)型,就會(huì)把參數(shù)放到map里面用collection(如果參數(shù)是List類(lèi)型還會(huì)再多放一個(gè)list名字的key和value)或者array作為key汹想,值作為value
3.如果有多個(gè)參數(shù)外邓,就會(huì)把參數(shù)名和參數(shù)值分別作為key和value放到map里面再返回,值得注意的是mybatis還會(huì)在map里再給你放一個(gè)別名的key古掏,也就是param+參數(shù)位置(如過(guò)參數(shù)是兩個(gè)就是param1损话,param2分別對(duì)應(yīng)第一個(gè)和第二個(gè)參數(shù))
看到這里我們就可以得出一個(gè)結(jié)論,在mybatis的mapper.xml里面動(dòng)態(tài)取值槽唾,不光可以用#{參數(shù)名}這種方式還可以用#{param1}這種方式丧枪。
MapperMethod類(lèi)在分揀完目標(biāo)方法后就正式調(diào)用sqlSession來(lái)執(zhí)行SQL了,我們看一下sqlSession是如何執(zhí)行的庞萍。這里執(zhí)行的是selectOne()方法拧烦。從源碼中可以看到mybatis做查詢(xún)的時(shí)候除了selectCursor方法,其余的查詢(xún)最終都會(huì)用selectList方法去執(zhí)行:
public class DefaultSqlSession implements SqlSession {
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
......
}
mybatis執(zhí)行SQL語(yǔ)句的過(guò)程
可以看到executor正是我們上一篇分析出來(lái)的mybatis執(zhí)行SQL語(yǔ)句的核心組件钝计。也就是說(shuō)mybatis前面都在準(zhǔn)備食材恋博,現(xiàn)在才正式下鍋炒菜。
那么mybatis是如何炒菜的呢葵蒂?我們繼續(xù)看一下:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
......
}
Executor是一個(gè)接口永高,首先要搞清楚誰(shuí)是實(shí)現(xiàn)類(lèi)饲宛,我們上一篇分析的是如果沒(méi)有指定executorType,那么默認(rèn)的Executor就是SimpleExecutor。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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;
}
其實(shí)不是殴瘦,因?yàn)槟J(rèn)cacheEnabled是true辣吃,也就是說(shuō)executor是CachingExecutor,而CachingExecutor再把SimpleExecutor包裝起來(lái)别智。也就是說(shuō)mybatis在這個(gè)地方使用了一個(gè)簡(jiǎn)單的裝飾器模式,使SimpleExecutor增強(qiáng)了一個(gè)緩存功能敞恋。
我們?cè)賮?lái)看一下CachingExecutor是如何使用緩存的:
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
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;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
......
}
CachingExecutor在這里操作的cache是二級(jí)緩存丽啡,二級(jí)緩存策略我們放到后面再研究,這里我們知道二級(jí)緩存是存在MappedStatement這個(gè)組件里的硬猫,大概的邏輯就是查詢(xún)結(jié)果存在緩存里就直接返回补箍,不存在再去調(diào)用SimpleExecutor去執(zhí)行SQL語(yǔ)句。
我們繼續(xù)往下看SimpleExecutor的執(zhí)行邏輯:
public abstract class BaseExecutor implements Executor {
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
@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());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
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;
}
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 {
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;
}
......
}
SimpleExecutor繼承了BaseExecutor的query()方法啸蜜, BaseExecutor首先做的事情是從一級(jí)緩存localCache中去取值馏予,取不到的話(huà)再去調(diào)用子類(lèi)的doQuery方法去執(zhí)行SQL,拿到緩存結(jié)果后再放到localCache里面盔性。localCache緩存使用是有一定條件的,這個(gè)我們也放到后面再講呢岗。
繼續(xù)來(lái)看doQuery方法:
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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
......
}
public class Configuration {
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);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
}
doQuery()方法會(huì)先拿到一個(gè)默認(rèn)RoutingStatementHandler冕香,再去連接數(shù)據(jù)庫(kù)蛹尝。拿到數(shù)據(jù)庫(kù)連接后就是StatementHandler正式出場(chǎng)了。
先來(lái)看一下RoutingStatementHandler的構(gòu)造方法悉尾。
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
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());
}
}
}
可以看到RoutingStatementHandler實(shí)際上還不是真正去調(diào)用JDBC執(zhí)行SQL語(yǔ)句的組件突那,mybatis在這里用了一個(gè)簡(jiǎn)單的策略模式,真正去做事的StatementHandler是通過(guò)ms.getStatementType()來(lái)判斷出來(lái)的构眯。
執(zhí)行SQL語(yǔ)句的StatementHandler有三個(gè)策略類(lèi):
1.SimpleStatementHandler 執(zhí)行簡(jiǎn)單SQL語(yǔ)句的策略類(lèi)
2.PreparedStatementHandler 執(zhí)行預(yù)編譯SQL語(yǔ)句的策略類(lèi)
3.CallableStatementHandler 執(zhí)行存儲(chǔ)過(guò)程SQL語(yǔ)句的策略類(lèi)
我們的demo使用的是預(yù)編譯的SQL語(yǔ)句愕难,我們來(lái)看一下PreparedStatementHandler:
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
......
}
到這里mybatis終于沒(méi)有什么騷操作了,直接使用JDBC的PreparedStatement執(zhí)行SQL語(yǔ)句惫霸,然后把數(shù)據(jù)庫(kù)的返回值交給ResultHandler來(lái)處理了猫缭。
ResultHandler顧名思義就是來(lái)處理返回值的,由于這篇篇幅太長(zhǎng)了壹店,mybatis是如何來(lái)處理返回值的我們放到下一篇來(lái)講猜丹。總之mybatis終于執(zhí)行SQL了硅卢,我們接下來(lái)總結(jié)一下整個(gè)過(guò)程射窒。
- 從Configuration中獲得MapperProxyFactory
- MapperProxyFactory構(gòu)建定義接口的動(dòng)態(tài)代理對(duì)象
- 再就是動(dòng)態(tài)代理對(duì)象執(zhí)行查詢(xún)方法步驟:
本文涉及的源碼放在github上:
https://github.com/burgleaf/mybatis-study