Mybatis 基於註解Mapper源碼分析

來源:互聯網
上載者:User

標籤:highlight   nal   ram   parent   fragments   etc   沒有   methods   名稱   

     目前Mybatis除了可以通過XML配置SQL外還可以通過註解的形式配置SQL,本文中主要介紹了Mybatis是如何處理註解SQL映射的,通過源碼分析處理過程

 XML配置
<configuration><settings><setting name="defaultExecutorType" value="SIMPLE"/><setting name="useGeneratedKeys" value="true"/></settings><typeAliases><typeAlias type="org.apache.ibatis.submitted.blocking_cache.Person" alias="Person" /> </typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"><property name="" value="" /></transactionManager><dataSource type="UNPOOLED"><property name="driver" value="org.hsqldb.jdbcDriver" /><property name="url" value="jdbc:hsqldb:mem:cache" /><property name="username" value="sa" /></dataSource></environment></environments><mappers><mapper class="org.apache.ibatis.submitted.blocking_cache.PersonMapper"/></mappers></configuration> 
  解析過程  

 

  private void mapperElement(XNode parent) throws Exception {    //如果configuration中配置了mapper節點    if (parent != null) {      for (XNode child : parent.getChildren()) {        //如果配置對是包路徑        if ("package".equals(child.getName())) {          String mapperPackage = child.getStringAttribute("name");          configuration.addMappers(mapperPackage);        } else {          //擷取mapper元素中的resource屬性          String resource = child.getStringAttribute("resource");          //擷取mapper元素中的url屬性          String url = child.getStringAttribute("url");          //擷取mapper元素中的class屬性,如果基於註解的配置的mapper 配置的就是class          String mapperClass = child.getStringAttribute("class");          //對於resource,url,mapperClass 優先使用resource,其次是url最後是class          if (resource != null && url == null && mapperClass == null) {            //建立異常上下文            ErrorContext.instance().resource(resource);            InputStream inputStream = Resources.getResourceAsStream(resource);            //建立XMLMapperBuilder            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());            //解析XML            mapperParser.parse();          } else if (resource == null && url != null && mapperClass == null) {            ErrorContext.instance().resource(url);            InputStream inputStream = Resources.getUrlAsStream(url);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());            mapperParser.parse();          } else if (resource == null && url == null && mapperClass != null) {            //擷取mapper的介面            Class<?> mapperInterface = Resources.classForName(mapperClass);            //將該介面註冊到已知mapper            configuration.addMapper(mapperInterface);          } else {            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");          }        }      }    }  }

  

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

  

//註冊Mapper  public <T> void addMapper(Class<T> type) {    //如果type是介面    if (type.isInterface()) {      //判斷該介面是否已經註冊過相應的Mapper了,如果是則拋出異常,因為knownMappers的key為type      if (hasMapper(type)) {        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");      }      boolean loadCompleted = false;      try {        //對該介面建立MapperProxyFactory,並保註冊到knownMappers        knownMappers.put(type, new MapperProxyFactory<T>(type));        //建立MapperAnnotationBuilder        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);        //解析Annotation        parser.parse();        //解析成功則表示載入完成        loadCompleted = true;      } finally {        //如果載入沒有完成則將其從knownMappers中刪除        if (!loadCompleted) {          knownMappers.remove(type);        }      }    }  }

  

public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace(‘.‘, ‘/‘) + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
//初始化註解類型,分為兩組
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);

sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}

public void parse() {
String resource = type.toString();
//判斷該資源是否已經註冊
if (!configuration.isResourceLoaded(resource)) {
//沒有註冊過則需要載入XML資源
loadXmlResource();
//將該資源名稱添加到已經註冊集合中
configuration.addLoadedResource(resource);
//設定nameSpace
assistant.setCurrentNamespace(type.getName());
//解析cache
parseCache();
//解析cacheRef
parseCacheRef();
//擷取
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
//如果不是bridge方法則解析statement
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
//解析待定方法
parsePendingMethods();
}

  

private void loadXmlResource() {    //判斷資源是否有載入過    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {      //擷取資源的path      String xmlResource = type.getName().replace(‘.‘, ‘/‘) + ".xml";      InputStream inputStream = null;      try {        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);      } catch (IOException e) {        // ignore, resource is not required      }      //如果資源存在      if (inputStream != null) {        //構建XMLMapperBuilder        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());        //解析XML        xmlParser.parse();      }    }  }

  

 private void parseCache() {    //擷取介面上 @CacheNamespace註解    CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);    //如果註解存在    if (cacheDomain != null) {      //擷取註解配置的緩衝大小      Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();      //擷取緩衝重新整理頻率      Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();      //解析CacheNamespace配置的屬性      Properties props = convertToProperties(cacheDomain.properties());      //使用註解配置的資料建立緩衝      assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);    }  }

  

private void parseCacheRef() {    //擷取介面上 @CacheNamespaceRef 註解    CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);    //如果配置了緩衝引用    if (cacheDomainRef != null) {      //擷取引用的類型      Class<?> refType = cacheDomainRef.value();      String refName = cacheDomainRef.name();      //如果參考型別和引用名稱都為空白則拋出異常      if (refType == void.class && refName.isEmpty()) {        throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");      }      //如果參考型別和引用名稱同時配置了有效資料則拋出異常,這兩個是互斥資料      if (refType != void.class && !refName.isEmpty()) {        throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");      }      //擷取namespace      String namespace = (refType != void.class) ? refType.getName() : refName;      //使用緩衝      assistant.useCacheRef(namespace);    }  }

  

  void parseStatement(Method method) {    //擷取參數類型    Class<?> parameterTypeClass = getParameterType(method);    //擷取LanguageDriver    LanguageDriver languageDriver = getLanguageDriver(method);    //從註解中擷取Sqlsource    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);    //如果sqlSource不為null    if (sqlSource != null) {      //擷取 @Options註解      Options options = method.getAnnotation(Options.class);      //建立statementId      final String mappedStatementId = type.getName() + "." + method.getName();      Integer fetchSize = null;      Integer timeout = null;      StatementType statementType = StatementType.PREPARED;      ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;      //擷取sql類型      SqlCommandType sqlCommandType = getSqlCommandType(method);      //是否是查詢      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;      //是否需要重新整理緩衝,如果不是查詢預設值為true,查詢預設值為false      boolean flushCache = !isSelect;      //是否使用緩衝,查詢預設為true,不是查詢預設為false      boolean useCache = isSelect;      KeyGenerator keyGenerator;      String keyProperty = "id";      String keyColumn = null;      //如果是插入或更新      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {        //擷取SelectKey註解        SelectKey selectKey = method.getAnnotation(SelectKey.class);        if (selectKey != null) {          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);          keyProperty = selectKey.keyProperty();        } else if (options == null) {          keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;        } else {          keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;          keyProperty = options.keyProperty();          keyColumn = options.keyColumn();        }      } else {//不是插入或更新 即查詢和刪除則keyGenerator為NoKeyGenerator執行個體        keyGenerator = NoKeyGenerator.INSTANCE;      }      //如果@Options註解不為null      if (options != null) {        //根據配置的值設定是否重新整理緩衝        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {          flushCache = true;        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {          flushCache = false;        }        //是否使用緩衝        useCache = options.useCache();        //fetchSize        fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348        timeout = options.timeout() > -1 ? options.timeout() : null;        statementType = options.statementType();        resultSetType = options.resultSetType();      }      String resultMapId = null;      //擷取ResultMap註解      ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);      if (resultMapAnnotation != null) {        String[] resultMaps = resultMapAnnotation.value();        StringBuilder sb = new StringBuilder();        for (String resultMap : resultMaps) {          if (sb.length() > 0) {            sb.append(",");          }          sb.append(resultMap);        }        resultMapId = sb.toString();      } else if (isSelect) {        resultMapId = parseResultMap(method);      }      assistant.addMappedStatement(          mappedStatementId,          sqlSource,          statementType,          sqlCommandType,          fetchSize,          timeout,          // ParameterMapID          null,          parameterTypeClass,          resultMapId,          getReturnType(method),          resultSetType,          flushCache,          useCache,          // TODO gcode issue #577          false,          keyGenerator,          keyProperty,          keyColumn,          // DatabaseID          null,          languageDriver,          // ResultSets          options != null ? nullOrEmpty(options.resultSets()) : null);    }  }

  

private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {    try {      //擷取@Select, @Insert, @Update, @Delete類型的註解       Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);      //擷取 @SelectProvider, @InsertProvider, @UpdateProvider @DeleteProvider註解      Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);      //如果SQL註解不為null      if (sqlAnnotationType != null) {        //同時sqlProvider註解也不為空白則拋出異常,兩者互斥        if (sqlProviderAnnotationType != null) {          throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());        }        //擷取註解        Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);        //擷取註解配置的值        final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);        //根據配置的資料建立SqlSource        return buildSqlSourceFromStrings(strings, parameterType, languageDriver);      } else if (sqlProviderAnnotationType != null) {//如果SqlProvider註解不為空白        Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);        //建立一個ProviderSqlSource        return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);      }      //如果沒有配置Sql註解也沒有配置SqlProvider註解則返回null      return null;    } catch (Exception e) {      throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);    }  }

  

 

Mybatis 基於註解Mapper源碼分析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.