MyBatis注解@Select@Update分析

Posted 叶长风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis注解@Select@Update分析相关的知识,希望对你有一定的参考价值。

MyBatis注解@Select、@Update分析

前面几篇文章分别分析了Mybatis中的Configuration的配置信息,MyBatis中的Mapper调用等等,在分析配置信息时只是讲了如何解析xml中的sql查询,但是并没有讲怎么解析Mapper中注解对应的SQL,就是如下:

@ResultMap("BaseResultMap")
@Select("select id, username, password, age, mobile, hotel_address from user where id=#id;")
User getUser2(@Param("id") Long id);

这一节就是具体来说如何解析注解,并将其保存进mappedStatements中。

1. 解析configuration


我们回到解析mapper的源码所在,为mapperElement(root.evalNode(“mappers”));在mapperElement方法中,无论是哪一种条件,最终会有一个addMapper的操作,所以这里直接看addMapper操作即可,我们转到最终的addMapper方法。

 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.put(type, new MapperProxyFactory(type));操作,同时进行了MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);对于注解提供的方法就是在这里开始解析的,我们进入到parse()方法中。

public void parse() 
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) 
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) 
        try 
          // issue #237
          if (!method.isBridge()) 
            parseStatement(method);
          
         catch (IncompleteElementException e) 
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        
      
    
    parsePendingMethods();
  

进入到parseStatement中,查看解析注释细节,如下:

  void parseStatement(Method method) 
   final String mappedStatementId = type.getName() + "." + method.getName();
    //获取到具体SQL
    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
      //获取到resultMap
      String resultMapId = null;
      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);
      
	  //将对应sqlSource以及其他参数保存进Map中
      assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          // ParameterMapID
          null,
          parameterTypeClass,
          resultMapId,
          getReturnType(method));
    
  

这里根据Mapper所在的包名加上对应解析的方法名,最终组装成mappedStatementId,也就是最终保存进map中的key。

在组装完对应的key、value sqlSource等参数后,调用addMappedStatement进行进一步的包装,再看addMappedStatement方法。

public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) 

    if (unresolvedCacheRef) 
      throw new IncompleteElementException("Cache-ref not yet resolved");
    

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) 
      statementBuilder.parameterMap(statementParameterMap);
    

    MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
    return statement;
  

这个方法中在经过将cache、timeout、sql、resource、fetchSize等等参数进行包装后生成MappedStatement对象,最后通过configuration.addMappedStatement(statement)方法,将statement保存进mappedStatements Map中。

对注解形式提供的SQL的分析就到此为止了。

以上是关于MyBatis注解@Select@Update分析的主要内容,如果未能解决你的问题,请参考以下文章

什么是 MyBatis 的接口绑定?有哪些实现方式?

MyBatis源码分析之@SelectProvider注解使用详解

Mybatis 基于注解Mapper源码分析

MyBatis源码分析之@ResultMap注解详解

阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_3 基于注解的自定义再分析

阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_2 回顾自定义mybatis的流程分析