• Mybatis 基于注解Mapper源码分析


         目前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> 
    

     

     @Insert("insert into table2 (name) values(#{name})")
        @SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
        int insertTable2(Name name);
        
        @Insert("insert into table2 (name) values(#{name})")
        @Options(useGeneratedKeys=true, keyProperty="nameId,generatedName", keyColumn="ID,NAME_FRED")
        int insertTable2WithGeneratedKey(Name name);
    

      

     解析过程 

     

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

      

    如果存在XML配置也是会加载的  

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

      下图是同时存在XML和annotation的情况

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

      

  • 相关阅读:
    洛谷 P3613 【深基15.例2】寄包柜
    洛谷 P1478 陶陶摘苹果(升级版)
    P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
    c++优先队列(priority_queue)用法详解
    洛谷 P3817 小A的糖果
    洛谷 P1208 [USACO1.3]混合牛奶 Mixing Milk
    洛谷 P1449 后缀表达式
    洛谷 P1106 删数问题
    在Linux中使用tomcat部署项目
    jar在linux上运行脚本 #start #stop #restart
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8908536.html
Copyright © 2020-2023  润新知