[TOC]
1.回顧Mybatis執(zhí)行sql的流程
在之前的代碼中我們的運(yùn)行過(guò)程再梳理一下,首先我們執(zhí)行Test剖毯,調(diào)用dao接口方法
接口的定義:
調(diào)用接口的實(shí)現(xiàn)類方法:
最后才是調(diào)用真正的sql:
上面的代碼是在接口實(shí)現(xiàn)類里面自己去執(zhí)行id旺坠,查找并執(zhí)行mapper文件里面的sql,那么我們想是不是可以減少一步呢然遏?
如果我們不用自己實(shí)現(xiàn)接口贫途,只需要將接口的名字和mapper文件的namespace對(duì)應(yīng)起來(lái),將接口里面的方法名與sql語(yǔ)句標(biāo)簽的id對(duì)應(yīng)起來(lái)是不是就可以了呢待侵?
事實(shí)上丢早,mybatis提供了這樣的做法,這就是mapper動(dòng)態(tài)代理秧倾。
2.mapper動(dòng)態(tài)代理怎么寫怨酝?
首先主配置文件(Mybatis.xml
),在里面配置數(shù)據(jù)庫(kù)連接信息那先,注冊(cè)需要掃描的mapper
文件:
定義數(shù)據(jù)庫(kù)查詢的接口农猬,里面每一個(gè)接口的名字很重要,需要和mapper
里面每一條sql
對(duì)應(yīng)起來(lái):
定義mapper
文件(namespace是接口的全限定類名):
那我們?cè)谑褂玫臅r(shí)候售淡,需要使用sqlSession.getMapper()
方法斤葱,里面?zhèn)魅氲氖墙涌冢馑际峭ㄟ^(guò)接口的全限定名揖闸,也就是前面在mapper.xml
文件里面配置的命名空間nameSpace
,這樣一來(lái)揍堕,就是獲取到了代理類,將dao
和mapper.xml
文件關(guān)聯(lián)起來(lái)了汤纸,而每條sql
的id
與我們的接口方法名字對(duì)應(yīng)起來(lái))
我們?cè)谇懊孢€寫到過(guò)一個(gè)selectStudentMap()
方法衩茸,但是里面調(diào)用的是和SelectList()
一樣的sql
,在接口的實(shí)現(xiàn)類里面我們自己處理了一下蹲嚣,但是現(xiàn)在使用自動(dòng)實(shí)現(xiàn)的話,底層只會(huì)調(diào)用SelectOne()
或者SelectList()
方法祟牲,所以這個(gè)方法會(huì)報(bào)錯(cuò)隙畜,如果接受類型是list
,那么框架會(huì)自動(dòng)使用selectList()
方法说贝,否則就會(huì)選擇selectOne()
這個(gè)方法议惰。
在這里我們使用的是返回的是map
,所以自動(dòng)選擇返回selectOne()
方法乡恕,那么就會(huì)報(bào)錯(cuò)言询。如果我們需要使用自動(dòng)返回map
的話俯萎,可以自己定一個(gè)map
,或者返回list
之后再處理运杭,這個(gè)知識(shí)點(diǎn)后面再介紹夫啊,有興趣可以訪問(wèn):mybatis的mapper返回map結(jié)果集
3.mapper動(dòng)態(tài)代理怎么做的?
打一個(gè)斷點(diǎn)在sqlSession.getMapper()
方法上:
我們可以看到執(zhí)行下面的接口方法(接口SqlSession
的方法)
<T> T getMapper(Class<T> var1);
這是一個(gè)接口辆憔,我們可以看到實(shí)現(xiàn)接口的有兩個(gè)類撇眯,一個(gè)是DefaultSqlSession
,一個(gè)是SqlSessionManager
,我們需要看的是DefaultSqlSession
下面的接口:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
我們知道虱咧,在創(chuàng)建sqlsession
的時(shí)候熊榛,confiiguration
這個(gè)配置對(duì)象已經(jīng)創(chuàng)建完成。跟進(jìn)去,這是使用mapper
注冊(cè)器對(duì)象的getMapper()
方法腕巡,將當(dāng)前的sqlSession
對(duì)象傳遞進(jìn)去:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
我們跟進(jìn)去源碼玄坦,可以發(fā)現(xiàn)里面使用knownMappers.get(type)
來(lái)獲取mapper
代理工廠,這個(gè)konwnMappers
是一個(gè)hashMap
绘沉,這個(gè)hashMap
里面已經(jīng)初始化了mapperProxyFactory
對(duì)象了煎楣,獲取到工廠對(duì)象之后,再去使用sqlSession
實(shí)例化:
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);
}
}
實(shí)例化的時(shí)候梆砸,使用了mapper
動(dòng)態(tài)代理:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
從下面的debug結(jié)果中我們可以看到转质,這是動(dòng)態(tài)代理的結(jié)果,我們看到的是dao
,但是動(dòng)態(tài)代理對(duì)這個(gè)dao
做了增強(qiáng)帖世,實(shí)則是一個(gè)mapperProxy
休蟹。
【作者簡(jiǎn)介】:
秦懷,公眾號(hào)【秦懷雜貨店】作者日矫,技術(shù)之路不在一時(shí)赂弓,山高水長(zhǎng),縱使緩慢哪轿,馳而不息盈魁。這個(gè)世界希望一切都很快,更快窃诉,但是我希望自己能走好每一步杨耙,寫好每一篇文章,期待和你們一起交流飘痛。