mybatis 之 mapper

柯星华
2023-12-01

目录

一、mapper解析

1、入口

2、解析

二、注册

三、获取

四、代理执行


一、mapper解析

1、入口

private void parseConfiguration(XNode root) {
  try {
    propertiesElement(root.evalNode("properties")); //issue #117 read properties first
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    settingsElement(root.evalNode("settings"));
    environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
      //解析mapper标签
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

2、解析

//  XMLConfigBuilder.mapperElement
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
            //resource配置方式走这里
          ErrorContext.instance().resource(resource);
            //根据路径读取文件流
          InputStream inputStream = Resources.getResourceAsStream(resource);
            //生成xmlMapperBuilder对象
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            //解析
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
            //rul配置方式走这里
          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) {
            //mapperClass方式走这里
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

//XmlMapperBuilder.parse()
public void parse() {
    //判断是否已经加载过
  if (!configuration.isResourceLoaded(resource)) {
      // xml -> configuration
    configurationElement(parser.evalNode("/mapper"));
      // 加入到已解析列表
    configuration.addLoadedResource(resource);
      //绑定命名空间
    bindMapperForNamespace();
  }
​
  parsePendingResultMaps();
  parsePendingChacheRefs();
  parsePendingStatements();
}

//解析mapper及子孙标签
//XmlMapperBuilder.configurationElement
private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace");
    if (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"));
    //解析sql片段
    sqlElement(context.evalNodes("/mapper/sql"));
    //解析  select|insert|update|delete 这几个标签
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

具体的解析select|insert|update|delete 标签

//XmlMapperBuilder.configurationElement
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}
​
​
​
public void parseStatementNode() {
    //这里省略了部分非必要代码逻辑    
    // 这里是生成sqlSource对象 ,这是sql的核心
    // sqlSource 的实现子类有DynamicSqlSource/staticSqlSource/RawSqlSource/ProviderSqlSource
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    // 省略非必要代码
    // 生成 MappedStatement 并加入configuration
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

二、注册

调用链如下:

1、confirguration.addMapper()

2、MapperRegistry.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 {
        //new 一个工厂类放到map里
      knownMappers.put(type, new MapperProxyFactory<T>(type));
        //
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

三、获取

调用链如下:

1、SqlSession.getMapper()

2、configuration.<T>getMapper(type, this);

3、mapperRegistry.getMapper(type, sqlSession);

4、mapperProxyFactory.newInstance(sqlSession); // 这就是刚才注册时 ,生成的工厂对象

5、mapperProxy.invoke();// 核心的逻辑

public T newInstance(SqlSession sqlSession) {
    //生成mapperProxy, 其实现了InvocationHandler , 是jdk的动态代理
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    //生成代理 , 注意 这里的代理是比较特殊的 , 因为其没有具体实现
  return newInstance(mapperProxy);
}
​
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

四、代理执行

MapperProxy.invoke()

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  if (Object.class.equals(method.getDeclaringClass())) {
    try {
        //如果是object 普通方法,直接调用
      return method.invoke(this, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
    //找到mapperMehtod对象 
    //这个对象里包装了一个mapper的sql标签
    /**
          private final SqlCommand command;
          private final MethodSignature method;
    */
  final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
  return mapperMethod.execute(sqlSession, args);
}

public Object execute(SqlSession sqlSession, Object[] args) {
    //已selectOne为例 , 其他代码忽略
   Object param = method.convertArgsToSqlCommandParam(args);
    //这里是通过sqlSession -> executor 来执行查询的
   result = sqlSession.selectOne(command.getName(), param);
}
 类似资料: