Mybatis【11】-- Mybatis Mapper動態代理怎麼寫?
1.回顧Mybatis執行sql的流程
在之前的程式碼中我們的執行過程再梳理一下,首先我們執行Test,呼叫dao介面方法
介面的定義:
呼叫介面的實現類方法:
最後才是呼叫真正的sql:
上面的程式碼是在介面實現類裡面自己去執行id,查詢並執行mapper檔案裡面的sql,那麼我們想是不是可以減少一步呢?
如果我們不用自己實現介面,只需要將介面的名字和mapper檔案的namespace對應起來,將接口裡面的方法名與sql語句標籤的id對應起來是不是就可以了呢?
事實上,mybatis提供了這樣的做法,這就是mapper動態代理。
2.mapper動態代理怎麼寫?
首先主配置檔案(Mybatis.xml
),在裡面配置資料庫連線資訊,註冊需要掃描的mapper
檔案:
定義資料庫查詢的介面,裡面每一個介面的名字很重要,需要和mapper
裡面每一條sql
對應起來:
定義mapper
檔案(namespace是介面的全限定類名):
那我們在使用的時候,需要使用sqlSession.getMapper()
方法,裡面傳入的是介面,意思是通過介面的全限定名,也就是前面在mapper.xml
檔案裡面配置的名稱空間nameSpace
,這樣一來,就是獲取到了代理類,將dao
和mapper.xml
檔案關聯起來了,而每條sql
的id
與我們的介面方法名字對應起來)
我們在前面還寫到過一個selectStudentMap()
方法,但是裡面呼叫的是和SelectList()
一樣的sql
,在介面的實現類裡面我們自己處理了一下,但是現在使用自動實現的話,底層只會呼叫SelectOne()
或者SelectList()
方法,所以這個方法會報錯,如果接受型別是list
,那麼框架會自動使用selectList()
方法,否則就會選擇selectOne()
這個方法。
在這裡我們使用的是返回的是map
,所以自動選擇返回selectOne()
方法,那麼就會報錯。如果我們需要使用自動返回map
的話,可以自己定一個map
,或者返回list
之後再處理,這個知識點後面再介紹,有興趣可以訪問:
3.mapper動態代理怎麼做的?
打一個斷點在sqlSession.getMapper()
方法上:
我們可以看到執行下面的介面方法(介面SqlSession
的方法)
<T> T getMapper(Class<T> var1);
這是一個介面,我們可以看到實現介面的有兩個類,一個是DefaultSqlSession
,一個是SqlSessionManager
,我們需要看的是DefaultSqlSession
下面的介面:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
我們知道,在建立sqlsession
的時候,confiiguration
這個配置物件已經建立完成。跟進去,這是使用mapper
註冊器物件的getMapper()
方法,將當前的sqlSession
物件傳遞進去:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
我們跟進去原始碼,可以發現裡面使用knownMappers.get(type)
來獲取mapper
代理工廠,這個konwnMappers
是一個hashMap
,這個hashMap
裡面已經初始化了mapperProxyFactory
物件了,獲取到工廠物件之後,再去使用sqlSession
例項化:
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);
}
}
例項化的時候,使用了mapper
動態代理:
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結果中我們可以看到,這是動態代理的結果,我們看到的是dao
,但是動態代理對這個dao
做了增強,實則是一個mapperProxy
。
【作者簡介】:
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界希望一切都很快,更快,但是我希望自己能走好每一步,寫好每一篇文章,期待和你們一起交流。
此文章僅代表自己(本菜鳥)學習積累記錄,或者學習筆記,如有侵權,請聯絡作者核實刪除。人無完人,文章也一樣,文筆稚嫩,在下不才,勿噴,如果有錯誤之處,還望指出,感激不盡~