Mybatis——Mapper解析

Mapper的注册入口在Configuration的addMapper方法中,其会调用MapperRegistry的addMapper方法。

Mapper的注册过程分为两个步骤:

1.创建MapperProxyFactory,将其与mapper的class进行映射

2.解析mapper对应xml文件和其方法上的注解,生成MappedStatement。

public class MapperRegistry {
	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 {
      	//1.创建MapperProxyFactory,将其与mapper的class进行映射
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        
        2.解析mapper对应xml文件和其方法上的注解,生成MappedStatement。
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
}

MapperAnnotationBuilder解析过程

MapperAnnotationBuilder的解析过程分为两个步骤:

1.解析xml文件

2.解析mapper方法上的注解

public void parse() {
	//校验mapper是否已经解析
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      //1.解析xml文件
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      //2.解析mapper方法上的注解
      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));
        }
      }
    }
    //3.重新解析由于首次解析因为父元素未解析导致解析的方法
    parsePendingMethods();
  }

xml文件解析

private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      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 xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

xml文件的路径为:String xmlResource = type.getName().replace('.', '/') + ".xml";mapper的全路径,并将后缀改为xml,因为要求xml文件与mapper class文件位于相同的路径下。

xmlParser.parse()解析流程:

public void parse() {
	//mapper节点解析
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
	//解析由于父ResultMap未解析的ResultMap
    parsePendingResultMaps();
    parsePendingCacheRefs();
    //解析由于依赖元素未解析的Insert/update/delete/select节点
    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"));
      //解析parameterMap节点为ParameterMap,存放于configuration的parameterMaps中
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap节点为ResultMap,存放于configuration的ResultMaps中
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql片段,存放在configuration的sqlFragments中
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析select|insert|update|delete节点生成MappedStatement,存放在configuration的mappedStatements中
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

method注解解析

void parseStatement(Method method) {
	//获取parameterType,如果有多个参数为ParamMap,单个参数为参数类型
    Class<?> parameterTypeClass = getParameterType(method);
    
    LanguageDriver languageDriver = getLanguageDriver(method);
    
    //解析@Select|Update|Delete|Insert中的value值,或者@SelectProvider|@UpdateProvider|@DeleteProvider|@InsertProvider的方法,生成SqlSource
    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
    
    if (sqlSource != null) {
      //解析@Options
      Options options = method.getAnnotation(Options.class);
      final String mappedStatementId = type.getName() + "." + method.getName();
      Integer fetchSize = null;
      Integer timeout = null;
      StatementType statementType = StatementType.PREPARED;
      ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
      SqlCommandType sqlCommandType = getSqlCommandType(method);
      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      boolean flushCache = !isSelect;
      boolean useCache = isSelect;
	  
      //解析@SelectKey生成KeyGenerator
      KeyGenerator keyGenerator;
      String keyProperty = "id";
      String keyColumn = null;
      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // first check for SelectKey annotation - that overrides everything else
        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.INSTANCE;
      }

      if (options != null) {
        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
          flushCache = true;
        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
          flushCache = false;
        }
        useCache = options.useCache();
        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();
      }
	  
	  //解析@ResultMap获取ResultMapId,或者解析@Resutls生成ResultMap
      String resultMapId = null;
      ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
      if (resultMapAnnotation != null) {//解析@ResultMap获取ResultMapId
        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) {//解析@Resutls生成ResultMap
        resultMapId = parseResultMap(method);
      }
	  
	  //根据上面解析得到的ResultMap、ParamterType、SqlSource等生成MapperStatement
      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);
    }
  }

ParameterMap解析

ParameterMap结构:

ParameterMap映射节点:

<parameterMap id="BaseParameterMap" type="com.example.demo.User">
        <parameter property="id" javaType="long" jdbcType="BIGINT"/>
        <parameter property="username" javaType="string" jdbcType="VARCHAR"/>
        <parameter property="password" javaType="string" jdbcType="VARCHAR"/>
</parameterMap>

parameterMap对应上面xml配置节点,该节点转换的parameterMap会注册到configuration中。解析逻辑在org.apache.ibatis.builder.xml.XMLMapperBuilder#parameterMapElement中。

此外还有select|insert|update|delete的parameter节点的parameterType属性,method注解的参数都是转换为parameterMap,但是只有id和type属性,且不会注册到configuration中,只会存放于mappedStatement中。生成逻辑位于org.apache.ibatis.builder.MapperBuilderAssistant#getStatementResultMaps中

ResultMap解析

1591951415295

resultMap映射节点:

xml

<resultMap id="BaseResultMap" type="com.example.demo.User">
    <id column="id" property="id" />
    <result column="username" property="username" />
    <result column="password" property="password" />
</resultMap>

注解

@Results(id = "BaseResultMap2",
            value = {
                    @Result(property = "id", column = "id"),
                    @Result(property = "username", column = "username"),
                    @Result(property = "password", column = "password"),
            }
    )

xml解析逻辑位于org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode, java.util.List<org.apache.ibatis.mapping.ResultMapping>)

注解解析逻辑位于org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseResultMap

解析结果会注册到Configuration的resultMaps中

SqlSource解析

1591952978391

1.RawSqlSource:纯sql语句,不包含动态判断节点,例如:

select * from user where id = #{id}

创建过程如下为:

	@Test
    public void testRawSqlSource(){
        RawSqlSource rawSqlSource = new RawSqlSource(new Configuration(), "select * from user where id = #{id}", Long.class);
        System.out.println(rawSqlSource.getBoundSql(1L).getSql());
    }

2.DynamicSqlSource:动态sql语句,例如:

select * from user where 
<if test="id != null">
      id = #{id}
</if>

创建过程如下为:

	@Test
    public void testDynamicSqlSource(){
        List<SqlNode> contents = new ArrayList<>();
        contents.add(new StaticTextSqlNode("select * from user where 1=1"));
        contents.add(new IfSqlNode(new StaticTextSqlNode("and id = #{id}"), "id != null"));
        MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
        DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(new Configuration(), mixedSqlNode);
        BoundSql boundSql = dynamicSqlSource.getBoundSql(null);
        System.out.println(boundSql.getSql());
        MapperMethod.ParamMap<Object> objectParamMap = new MapperMethod.ParamMap<>();
        objectParamMap.put("id", 1);
        System.out.println(dynamicSqlSource.getBoundSql(objectParamMap).getSql());
    }

1592205601819

3.ProviderSqlSource:@SelectProvider|@UpdateProvider|@DeleteProvider|@InsertProvider修饰注解方法,例如:

	@Results(id = "BaseResultMap2",
            value = {
                    @Result(property = "id", column = "id"),
                    @Result(property = "username", column = "username"),
                    @Result(property = "password", column = "password"),
            }
    )
    @SelectProvider(type = UserSelectProvider.class, method = "selectById")
    User selectById2(@Param("id") Long id);

    public class UserSelectProvider{
        
        public String selectById(){
            return new SQL().SELECT("username", "password")
                    .FROM("user")
                    .WHERE("id = #{id}").toString();
        }

    }

创建过程如下:

	@Test
    public void testProviderSqlSource() {
        Method[] methods = UserMapper.class.getMethods();
        for (Method method : methods) {
            SelectProvider selectProvider = method.getAnnotation(SelectProvider.class);
            if(selectProvider != null) {
                ProviderSqlSource providerSqlSource = new ProviderSqlSource(new Configuration(), selectProvider, UserMapper.class, method);
                System.out.println(providerSqlSource.getBoundSql(null).getSql());
            }
        }
    }

4.StaticSqlSource:静态sqlSource,内部String类型的sql语句,上述3种最终都会转换为该SqlSource

解析逻辑位于org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode

MappedStatement解析

1591952577873

mappedStatement映射节点:

xml

<select id="selectById" resultType="com.example.demo.User" parameterType="long">    	select * from user where id = #{id};
</select>

注解

	@Results(id = "BaseResultMap2",
            value = {
                    @Result(property = "id", column = "id"),
                    @Result(property = "username", column = "username"),
                    @Result(property = "password", column = "password"),
            }
    )
    @Select("select * from user")
    @Options(fetchSize = 10)
    User selectById2(@Param("id") Long id);

mappedStatement的主要字段:

1.sqlCommandType:由使用的标签Select|Insert|Update|Delete等决定

2.parameterMap:传入参数,由属性parameterType|parameterMap决定

3.resultMaps:返回结果集,由属性resultMap|resultType决定

4.sqlSource:标签包围的sql节点,即:select * from user where id = #{id}

xml解析逻辑位于org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode

注解解析逻辑位于org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseStatement

整体创建一个MappedStatement如下:

<select id="selectById" resultType="com.example.demo.User" parameterType="long">    	select * from user where id = #{id};
</select>

	@Test
    public void testMappedStatement() {
        List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
        ParameterMap parameterMap = new ParameterMap.Builder(
                configuration,
                "selectById-Inline",
                Long.class,
                parameterMappings).build();

        ResultMap inlineResultMap = new ResultMap.Builder(
                configuration,
                "selectById-Inline",
                User.class,
                new ArrayList<ResultMapping>(),
                null).build();


        RawSqlSource rawSqlSource = new RawSqlSource(configuration, "select * from user where id = #{id}", Long.class);
        MappedStatement.Builder builder = new MappedStatement
                .Builder(configuration, "selectById", rawSqlSource, SqlCommandType.SELECT);
        builder.parameterMap(parameterMap);
        builder.resultMaps(Collections.singletonList(inlineResultMap));
        MappedStatement mappedStatement = builder.build();

        configuration.addMappedStatement(mappedStatement);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        System.out.println(sqlSession.selectOne("selectById", 18L));
    }

posted @ 2020-06-16 14:39  wuweishuo  阅读(797)  评论(0)    收藏  举报