1. 程式人生 > 實用技巧 >Mybatis【11】-- Mybatis Mapper動態代理怎麼寫?

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,這樣一來,就是獲取到了代理類,將daomapper.xml檔案關聯起來了,而每條sqlid與我們的介面方法名字對應起來)

我們在前面還寫到過一個selectStudentMap()方法,但是裡面呼叫的是和SelectList()一樣的sql,在介面的實現類裡面我們自己處理了一下,但是現在使用自動實現的話,底層只會呼叫SelectOne()或者SelectList()方法,所以這個方法會報錯,如果接受型別是list,那麼框架會自動使用selectList()方法,否則就會選擇selectOne()這個方法。

在這裡我們使用的是返回的是map,所以自動選擇返回selectOne()方法,那麼就會報錯。如果我們需要使用自動返回map的話,可以自己定一個map,或者返回list之後再處理,這個知識點後面再介紹,有興趣可以訪問:

mybatis的mapper返回map結果集

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

【作者簡介】
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界希望一切都很快,更快,但是我希望自己能走好每一步,寫好每一篇文章,期待和你們一起交流。

此文章僅代表自己(本菜鳥)學習積累記錄,或者學習筆記,如有侵權,請聯絡作者核實刪除。人無完人,文章也一樣,文筆稚嫩,在下不才,勿噴,如果有錯誤之處,還望指出,感激不盡~