mybatis dao 映射

在使用mybatis時(shí),我們通常直接調(diào)用接口dao(也就是常說的mapper)中的方法呛梆,然后應(yīng)用會(huì)自動(dòng)調(diào)用xml文件中對(duì)應(yīng)標(biāo)簽(select |update| insert|delete)中id與dao中方法名相同的sql,dao通過xml文件中的namespace做映射师幕,調(diào)用dao即是調(diào)用xml中的sql隆圆。是怎么實(shí)現(xiàn)的呢?

SqlSessionFactoryBean

mybatis 通常與spring 一起使用俭识,配置SqlSessionFactoryBean

<bean class="org.mybatis.spring.SqlSessionFactoryBean">
                <!-- 實(shí)例化sqlSessionFactory時(shí)需要使用上述配置好的數(shù)據(jù)源以及SQL映射文件 -->
                <property name="dataSource" ref="mailaDataSource"/>
                <property name="mapperLocations" value="classpath*:mybatis/maila/**/*.xml"/>
                <property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"/>
                <property name="typeAliasesPackage" value="com.maila.biz.center.biz.entity,com.maila88.**.entity"/>
            </bean>

首先我們來看一下SqlSessionFactoryBean,該bean 實(shí)現(xiàn)了InitializingBean接口洞渔,初始化完成時(shí)會(huì)調(diào)用afterPropertiesSet方法

public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

rotected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;

    ...

}

buildSqlSessionFactory方法內(nèi)容很多套媚,大致上就是加載xml(xml包含sql的xml和setting設(shè)置的xml),解析xml等磁椒,包裝mybatis的configuration堤瘤。這里不做過多的介紹,這里我們?cè)敿?xì)介紹一樣dao與xml的映射問題浆熔。在buildSqlSessionFactory方法中有這么一段代碼:

if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
        //mapperLocation路徑轉(zhuǎn)換為xmlMappperBuilder
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
        //解析xml
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }

上述代碼把<propertyname="mapperLocations"value="classpath*:mybatis/maila/**/*.xml"/>
轉(zhuǎn)化為xmlMapperBuilder本辐,并加以parse();

parse()

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //設(shè)置xml中的標(biāo)簽元素
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //mapper 與namespace 綁定
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

這里我們?cè)敿?xì)看一下mapper 與namespace的綁定

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) {
        if (!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);
        }
      }
    }
  }

綁定做了幾件事情

  • 取得當(dāng)前的namaspace(即mapper接口)的全限定路徑名
  • 反射得到類
  • 類添加至mybatis configuration -- configuration.addMapper(boundType)

addMapper方法:

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

  public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

MapperRegistry的addMapper方法:

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();


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<T>(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);
        }
      }
    }
  }

knownMappers 以類為key,MapperProxyFactory 為value医增,也就是說:mybatis 為每個(gè)dao接口都創(chuàng)建了一個(gè)代理工廠慎皱。

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) {
    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);
  }

}

代理工廠實(shí)例化負(fù)責(zé)實(shí)例化每個(gè)接口dao,并用一個(gè)private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); 儲(chǔ)存dao中的方法調(diào)用叶骨,MapperMethod又是一個(gè)很大的內(nèi)容宝冕,后續(xù)再談。

至此邓萨,我們知道了mybatis configuration中有個(gè)mapperRegistry,mapperRegistry中有一個(gè)Map菊卷,保存了所有dao和他的代理缔恳。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洁闰,隨后出現(xiàn)的幾起案子歉甚,更是在濱河造成了極大的恐慌,老刑警劉巖扑眉,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纸泄,死亡現(xiàn)場(chǎng)離奇詭異赖钞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)聘裁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門雪营,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衡便,你說我怎么就攤上這事献起。” “怎么了镣陕?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵谴餐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我呆抑,道長(zhǎng)岂嗓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任鹊碍,我火速辦了婚禮厌殉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妹萨。我一直安慰自己年枕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布乎完。 她就那樣靜靜地躺著熏兄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪树姨。 梳的紋絲不亂的頭發(fā)上摩桶,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音帽揪,去河邊找鬼硝清。 笑死,一個(gè)胖子當(dāng)著我的面吹牛转晰,可吹牛的內(nèi)容都是我干的芦拿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼查邢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蔗崎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扰藕,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤缓苛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后邓深,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體未桥,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笔刹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冬耿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舌菜。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淆党,靈堂內(nèi)的尸體忽然破棺而出酷师,到底是詐尸還是另有隱情,我是刑警寧澤染乌,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布山孔,位于F島的核電站,受9級(jí)特大地震影響荷憋,放射性物質(zhì)發(fā)生泄漏台颠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一勒庄、第九天 我趴在偏房一處隱蔽的房頂上張望串前。 院中可真熱鬧,春花似錦实蔽、人聲如沸荡碾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坛吁。三九已至,卻和暖如春铐尚,著一層夾襖步出監(jiān)牢的瞬間拨脉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工宣增, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玫膀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓爹脾,卻偏偏與公主長(zhǎng)得像帖旨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灵妨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容