[Hand tear código fuente de MyBatis] Proceso de inicio de MyBatis, configuración del sistema de configuración

Proceso de inicio de MyBatis

El proceso general es el siguiente:

  • Cargar archivo XML de configuración
  • Lea el archivo de descripción dtd de mybatis y analice la etiqueta xml
  • Genere el objeto de configuración global correspondiente leyendo la información de configuración XML y genere la asignación de SQL que requiere el asignador.
  • Después de crear SqlSessionFactory, use SqlSessionFactory para crear Session.

inserte la descripción de la imagen aquí

  • Configuración: Es el objeto central del proceso de inicialización de Mybatis.Casi toda la información de configuración en Mybatis se guardará en Configuración y tendrá efecto globalmente.
  • XMLConfigBuilder: se utiliza para crear la configuración, analizar la información de configuración del nodo de configuración en el archivo de configuración de MyBatis y rellenarlo en el objeto de configuración
  • XMLMapperEntityResolver: Incluido en XMLConfigBuilder, utilizado para leer archivos DTD locales.
  • XPathParser: analizador XPath, que encapsula clases JDK y está incluido en XMLConfigBuilder. XPath es el lenguaje de ruta XML (Lenguaje de ruta XML), que es un lenguaje utilizado para determinar la ubicación de una determinada parte de un documento XML.
  • Documento: incluido en XPathParser, es conveniente leer el contenido del archivo de configuración más tarde combinado con XPathParser.

Crear SQLSessionFactory

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

public SqlSessionFactory build(InputStream inputStream) {
    
    
    return build(inputStream, null, null);
}

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    
    
    // 准备阶段:将配置文件加载到内存中并生成document对象,然后创建初始化Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
  // 解析document对象并生成 SqlSessionFactory
  //parser.parse()返回的就是一个Configuration对象,对应的就是下面的方法
    return build(parser.parse());
}

public SqlSessionFactory build(Configuration config) {
    
    
  return new DefaultSqlSessionFactory(config);
}

El SQLSessionFactory que creamos es en realidad DefaultSqlSessionFactory, y su creación requiere un objeto de configuración

Crear XMLConfigBuilder

Crear un SqlSessionFactory requiere:

  • Confíe en XMLConfigBuilder para leer y analizar archivos de configuración y convertir archivos de configuración en objetos de documento
  • Inicializa el objeto de configuración y rellena las propiedades del archivo de configuración.
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    
    
   this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    
    
   // 调用父类初始化configuration
   super(new Configuration());
   ErrorContext.instance().resource("SQL Mapper Configuration");    
   // 将Properties全部设置到configuration里面去
   this.configuration.setVariables(props);
   this.parsed = false;
   this.environment = environment;
  // 绑定 XPathParser
   this.parser = parser;
}

Al inicializar Configuración se registrará en el mapa typeAliases la clase a utilizar y su alias.

private final Map<String, Class<?>> typeAliases = new HashMap<>();
public Configuration() {
    
    
  typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  typeAliasRegistry.registerAlias("LRU", LruCache.class);
  typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

  typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  languageRegistry.register(RawLanguageDriver.class);
}

Crear XPathParser

XPathParser contiene muchos objetos importantes, incluidos objetos XMLMapperEntityResolver para leer archivos DTD locales, objetos XPath, objetos Document para almacenar archivos xml y variables de objeto de colección de pares clave-valor definidas por etiquetas de propiedades.

inserte la descripción de la imagen aquí

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    
    
  this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    
    
  // 初始化属性
  commonConstructor(validation, variables, entityResolver);
  // 读取xml文件,保存为 document
  this.document = createDocument(new InputSource(inputStream));
}
public class XPathParser {
    
    
  private final Document document;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;
}

Analizar y establecer propiedades en Configuración

El análisis de configuración de MyBatis se basa <configuration>en la etiqueta

Veamos un archivo de configuración:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--SqlSessionFactoryBuilder中配置的配置文件的优先级最高;config.properties配置文件的优先级次之;properties标签中的配置优先级最低 -->
    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>

    <!--一些重要的全局配置-->
    <settings>
    <setting name="cacheEnabled" value="true"/>
    <!--<setting name="lazyLoadingEnabled" value="true"/>-->
    <!--<setting name="multipleResultSetsEnabled" value="true"/>-->
    <!--<setting name="useColumnLabel" value="true"/>-->
    <!--<setting name="useGeneratedKeys" value="false"/>-->
    <!--<setting name="autoMappingBehavior" value="PARTIAL"/>-->
    <!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>-->
    <!--<setting name="defaultExecutorType" value="SIMPLE"/>-->
    <!--<setting name="defaultStatementTimeout" value="25"/>-->
    <!--<setting name="defaultFetchSize" value="100"/>-->
    <!--<setting name="safeRowBoundsEnabled" value="false"/>-->
    <!--<setting name="mapUnderscoreToCamelCase" value="false"/>-->
    <!--<setting name="localCacheScope" value="STATEMENT"/>-->
    <!--<setting name="jdbcTypeForNull" value="OTHER"/>-->
    <!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING" />-->
    </settings>

    <typeAliases>

    </typeAliases>

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果-->
            <!--如果某些查询数据量非常大,不应该允许查出所有数据-->
            <property name="pageSizeZero" value="true"/>
        </plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://10.59.97.10:3308/windty"/>
                <property name="username" value="windty_opr"/>
                <property name="password" value="windty!234"/>
            </dataSource>
        </environment>
    </environments>

    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql" />
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>

    <mappers>
        <!--这边可以使用package和resource两种方式加载mapper-->
        <!--<package name="包名"/>-->
        <!--<mapper resource="./mappers/SysUserMapper.xml"/>-->
        <mapper resource="./mappers/CbondissuerMapper.xml"/>
    </mappers>

</configuration>

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());

// XMLConfigBuilder
public Configuration parse() {
    
    
  // 根据parsed变量的值判断是否已经完成了对mybatis-config.xml配置文件的解析
  if (parsed) {
    
    
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // 在mybatis-config.xml配置文件中查找<configuration>节点,并开始解析
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

Continuamos con este método parseConfiguration:

private void parseConfiguration(XNode root) {
    
    
    try {
    
    
      //issue #117 read properties first
      //解析properties标签,并set到Configration对象中
      //在properties配置属性后,在Mybatis的配置文件中就可以使用${key}的形式使用了。
      propertiesElement(root.evalNode("properties"));
      
      //解析setting标签的配置
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //添加vfs的自定义实现,这个功能不怎么用
      loadCustomVfs(settings);
        
      //配置类的别名,配置后就可以用别名来替代全限定名
      //mybatis默认设置了很多别名,参考附录部分
      typeAliasesElement(root.evalNode("typeAliases"));
        
      //解析拦截器和拦截器的属性,set到Configration的interceptorChain中
      //MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
      //Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
        //ParameterHandler (getParameterObject, setParameters)
        //ResultSetHandler (handleResultSets, handleOutputParameters)
        //StatementHandler (prepare, parameterize, batch, update, query)
      pluginElement(root.evalNode("plugins"));
      
      //Mybatis创建对象是会使用objectFactory来创建对象,一般情况下不会自己配置这个objectFactory,使用系统默认的objectFactory就好了
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
       
      //设置在setting标签中配置的配置
      settingsElement(settings);
   
      //解析环境信息,包括事物管理器和数据源,SqlSessionFactoryBuilder在解析时需要指定环境id,如果不指定的话,会选择默认的环境;
      //最后将这些信息set到Configration的Environment属性里面
      environmentsElement(root.evalNode("environments"));
        
      //
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        
      //无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。解析typeHandler。
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析Mapper
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
    
    
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

El análisis del archivo de configuración requiere la expresión de xpath y el objeto del documento, hace coincidir el nodo según el nombre de la etiqueta, extrae el valor correspondiente, encuentra el nodo y devuelve el objeto XNode, y luego establece el valor correspondiente para Configuración

//XPathParser
public XNode evalNode(String expression) {
    
    
  return evalNode(document, expression);
}

Echemos un vistazo más de cerca al análisis de varios nodos importantes:

Analizar el nodo ambiental

Por ejemplo en nuestro archivo de configuración:

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

private void environmentsElement(XNode context) throws Exception {
    
    
  if (context != null) {
    
    
    // 未指定XMLConfigBuilder.environment字段,则使用default属性
    if (environment == null) {
    
    
      environment = context.getStringAttribute("default");
    }
    // 遍历子节点
    for (XNode child : context.getChildren()) {
    
    
      String id = child.getStringAttribute("id");
      // 与XmlConfigBuilder.environment字段匹配
      if (isSpecifiedEnvironment(id)) {
    
    
        // 创建TransactionFactory
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        // 创建DataSourceFactory和DataSource
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        DataSource dataSource = dsFactory.getDataSource();
        // 创建Environment
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        // 将Environment对象记录到Configuration.environment字段中
        configuration.setEnvironment(environmentBuilder.build());
        break;
      }
    }
  }
}

Parsing Mappers Tags

<mappers>
    <package name="com.daley.dao"/>
</mappers>

Si el nombre del nodo es igual al paquete

  • Escaneará automáticamente todos los mapeadores bajo la ruta del paquete,
  • El mapperRegistry en la configuración agregará la ruta del paquete correspondiente
  • Agregue MapperProxyFactory correspondiente al asignador final a knowMappers
private void mapperElement(XNode parent) throws Exception {
    
    
  if (parent != null) {
    
    
    // 处理mapper子节点
    for (XNode child : parent.getChildren()) {
    
    
      // package子节点
      if ("package".equals(child.getName())) {
    
    
        // 自动扫描包下所有映射器
        String mapperPackage = child.getStringAttribute("name");
        // 扫描指定的包,并向mapperRegistry注册mapper接口
        configuration.addMappers(mapperPackage);
      } else {
    
    
        
        // 直接指定Mapper属性
        // 获取mapper节点的resource、url、class属性,三个属性互斥
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        // 如果mapper节点指定了resource或者url属性,则创建XmlMapperBuilder对象,并通过该对象解析resource或者url属性指定的mapper配置文件
        if (resource != null && url == null && mapperClass == null) {
    
    
          // 使用类路径
          ErrorContext.instance().resource(resource);
          try(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) {
    
    
          // 使用绝对url路径
          ErrorContext.instance().resource(url);
          try(InputStream inputStream = Resources.getUrlAsStream(url)){
    
    
            // 创建XMLMapperBuilder对象,解析映射配置文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url == null && mapperClass != null) {
    
    
          // 如果mapper节点指定了class属性,则向MapperRegistry注册该mapper接口
          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.");
        }
      }
    }
  }
}

inserte la descripción de la imagen aquí

//MapperRegistry
public void addMappers(String packageName) {
    
    
    addMappers(packageName, Object.class);
  }
public void addMappers(String packageName, Class<?> superType) {
    
    
  // 查找包下所有父类是 Objects.class 的类
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  for (Class<?> mapperClass : mapperSet) {
    
    
    addMapper(mapperClass);
  }
}
// MapperRegistry.addMapper()
public <T> void addMapper(Class<T> type) {
    
    
 ……
    boolean loadCompleted = false;
    // 将Mapper接口对应的Class对象和MapperProxyFactory对象添加到knownMappers集合
    knownMappers.put(type, new MapperProxyFactory<>(type));
    
    // 对被注解接口的解析逻辑
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    parser.parse();
    loadCompleted = true;
}

Después de agregar la relación de mapeo del mapeador, comenzará a analizar las anotaciones relevantes en la interfaz del Mapeador.

  • Se leerá el archivo xml del asignador correspondiente a la clase del asignador y el contenido del archivo se analizará después de la lectura
  • Para cada método de la clase de mapeador de variables, lea las anotaciones marcadas para el análisis, como resultMap
public void parse() {
    
    
  String resource = type.toString();
  // 检测是否已经加载过该接口
  if (!configuration.isResourceLoaded(resource)) {
    
    
    // 检测是否加载过对应的映射配置文件,如果未加载,则创建XMLMapperBuilder对象解析对应的mapper映射xml文件
    loadXmlResource();
    configuration.addLoadedResource(resource);
    assistant.setCurrentNamespace(type.getName());
    // 解析@CacheNamespace注解
    parseCache();
    // 解析@CacheNamespaceRef注解
    parseCacheRef();
    //开始解析每个方法
    for (Method method : type.getMethods()) {
    
    
      if (!canHaveStatement(method)) {
    
    
        continue;
      }
      if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
          && method.getAnnotation(ResultMap.class) == null) {
    
    
        parseResultMap(method);
      }
      try {
    
    
        // 解析@SelectKey,@ResultMap等注解,并创建MappedStatement对象
        parseStatement(method);
      } catch (IncompleteElementException e) {
    
    
        // 如果解析过程出现IncompleteElementException异常,可能是引用了未解析的注解,此处将出现异常的方法添加到incompleteMethod集合中保存
        configuration.addIncompleteMethod(new MethodResolver(this, method));
      }
    }
  }
  // 遍历incompleteMethods集合中记录的未解析的方法,并重新进行解析
  parsePendingMethods();
}

El método loadXmlResource llamará al método parse para analizar el contenido en el mapeador del objeto xml.

public void parse() {
    
    
  // 判断是否已经加载过该映射文件
  if (!configuration.isResourceLoaded(resource)) {
    
    
    // 处理mapper节点
    configurationElement(parser.evalNode("/mapper"));
    // 将resource添加到Configuration.loadedResources集合中保存,他是hashset类型的集合,其中记录了已经加载过的映射文件
    configuration.addLoadedResource(resource);
    // 绑定映射器到namespace
    bindMapperForNamespace();
  }

  // 处理ConfigurationElement方法中解析失败的resultMap节点
  parsePendingResultMaps();
  // 处理ConfigurationElement方法中解析失败的cache-ref节点
  parsePendingCacheRefs();
  // 处理ConfigurationElement方法中解析失败的SQL语句节点
  parsePendingStatements();
}

método configurationElement: analizar nodos como sql, resultMap, parámetroMap, etc.:

private void configurationElement(XNode context) {
    
    
  try {
    
    
    // 获取mapper节点的namespace属性
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.isEmpty()) {
    
    
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    // 设置MapperBuilderAssistant的currentNamespace字段,记录当前命名空间
    builderAssistant.setCurrentNamespace(namespace);
    // 解析cache-ref节点
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析cache节点
    cacheElement(context.evalNode("cache"));
    // 解析parameterMap节点
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 解析resultMap节点
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // 解析sql节点
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析select、update、insert、delete等SQL节点
    // 就在这里开始创建sql与方法的映射关系并且保存
    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);
  }
}

Hasta ahora, se ha creado la Configuración y se devolverá el SqlSessionFactory creado. Resumamos:

inserte la descripción de la imagen aquí

El proceso de sesión abierta

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    
    Transaction tx = null;
    try {
    
    
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //获取执行器,这边获得的执行器已经代理拦截器的功能(见下面代码)
      final Executor executor = configuration.newExecutor(tx, execType);
      //根据获取的执行器创建SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
    
    
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
    
    
      ErrorContext.instance().reset();
    }
  }
//interceptorChain生成代理类,具体参见Plugin这个类的方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    
    
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
    
    
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
    
    
      executor = new ReuseExecutor(this, transaction);
    } else {
    
    
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
    
    
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

El ejecutor se divide en dos categorías, una es CacheExecutor y la otra es un ejecutor ordinario.
Los ejecutores ordinarios se dividen en tres ejecutores básicos, SimpleExecutor, ReuseExecutor y BatchExecutor.

  • SimpleExecutor: cada vez que se ejecuta una actualización o selección, se abre un objeto de declaración y el objeto de declaración se cierra inmediatamente cuando se agota.
  • ReuseExecutor: Ejecute update o select, use sql como la clave para encontrar el objeto Statement, utilícelo si existe y créelo si no existe. Después de su uso, el objeto Statement no se cerrará, sino que se colocará en Map<String, Statement> para el próximo uso. En resumen, el objeto Statement se reutiliza.
  • BatchExecutor: Ejecute la actualización (sin seleccionar, el procesamiento por lotes de JDBC no admite la selección), agregue todo el sql al procesamiento por lotes (addBatch()), espere la ejecución unificada (executeBatch()), almacena en caché varios objetos de declaración, y cada objeto de declaración espera para ejecutar el procesamiento por lotes de executeBatch() uno por uno después de que se completa addBatch(). Igual que el procesamiento por lotes de JDBC.
    Ámbito de acción: estas funciones de Executor están estrictamente limitadas dentro del ámbito del ciclo de vida de SqlSession.

CacheExecutor en realidad encapsula el Ejecutor ordinario. La diferencia con el Ejecutor ordinario es que primero verifica si hay un resultado en el caché antes de realizar la consulta. Si existe, usa el resultado en el caché. Si no existe, todavía usa el Ejecutor ordinario para consultar, y luego almacena el resultado de la consulta en el caché.

inserte la descripción de la imagen aquí

Descripción general de la configuración

Configuration es el centro de gestión centralizado de todo el sistema de configuración de MyBatis, la mayoría de los componentes mencionados anteriormente, como Executor, StatementHandler, Cache, MappedStatement, etc., son directa o indirectamente creados y gestionados por él. Además, la configuración de propiedades que afecta el comportamiento de estos componentes también se guarda y mantiene. Como cacheEnabled, lazyLoadingEnabled...etc. Así es muy imagen de la gran ama de llaves de MyBatis .

La configuración es una clase extremadamente importante en MyBatis, que representa la información de configuración global de MyBatis.

La configuración contiene mucha información clave, que incluye principalmente:

  1. entornos: almacene información de múltiples entornos, cada entorno tiene transaccionesFactory y dataSource;
  2. mapeadores: almacena la información de ubicación de todos los archivos del mapeador;
  3. mappedStatements: almacena toda la información de mappedStatement, cada mappedStatement representa una declaración SQL;
  4. resultMaps: almacena toda la información de resultMap y resultMap representa la asignación del conjunto de resultados;
  5. parámetroMap: utilizado por la versión anterior, en desuso;
  6. keyGenerators: almacena toda la información de keyGenerator, y keyGenerator se utiliza para generar claves primarias;
  7. typeHandlers: almacena toda la información de typeHandler, typeHandler se usa para la conversión entre javaType y jdbcType;
  8. objectFactory: fábrica de objetos, utilizada para instanciar el objeto de destino;
  9. objectWrapperFactory: fábrica de contenedores de objetos, utilizada para crear objetos proxy para objetos de destino;
  10. Interceptores: almacena toda la información de Interceptor, e Interceptor se usa para que los interceptores ejecuten SQL;
  11. databaseIdProvider: Obtener el MappedStatement correspondiente según el ID de la base de datos;
  12. logImpl: fábrica de registros;
  13. caché: espacio de nombres de caché;
  14. etc.

Toda la información guardada por Configuración constituye todo el entorno de configuración de una sesión MyBatis (SqlSession). Cada vez que se abre una nueva SqlSession, toda la información que necesita la SqlSession se construirá de acuerdo con la Configuración .

En resumen, Configuración almacena la información de configuración global de MyBatis, que representa un entorno de configuración de MyBatis, que incluye declaraciones de mapeo de SQL, mapeo de conjuntos de resultados, mecanismo de almacenamiento en caché y otra información, que constituye todo el contenido de configuración requerido por una sesión de MyBatis.

  • Cuando llamamos SqlSessionFactoryBuilder.build(reader)a build SqlSessionFactory,La capa inferior construirá un objeto de configuración de acuerdo con el archivo de configuración

  • Cuando llamamos para openSession()abrir una sesión, SqlSession consta de una Configuración y un Ejecutor. Esta Configuración es la información de configuración global construida anteriormente.

Entonces se puede ver que la Configuración es de gran importancia para MyBatis, que contiene todos los recursos de configuración y la información del entorno requerida por las sesiones de MyBatis. Sin configuración, SqlSession no se puede construir en absoluto y no puede funcionar.

La función principal y la fuente de configuración de Configuration

Configuración Hay tres fuentes de configuración:

  • El archivo de inicio Mybatis-config.xml, la configuración global y los componentes globales se derivan de esto.
  • El mapeo SQL Mapper.xml (MappedStatement)\el mapeo del conjunto de resultados (ResultMapper) todos provienen de aquí.
  • Otra forma de expresión para la asignación de @Annotation SQL y la asignación de conjuntos de resultados.

inserte la descripción de la imagen aquí

Luego, hablemos sobre el rol principal de la configuración:

  • Almacene información de configuración global, que proviene de la configuración (settings)
  • Inicializar y mantener componentes base globales
    • typeAliases (tipo de alias)
    • typeHandlers (manejadores de tipos)
    • complementos
    • entornos (configuración del entorno)
    • caché (espacio de caché secundario)
  • Inicializar y mantener MappedStatement
  • Generador de componentes, mejorado con complementos
    • nuevoEjecutor (ejecutor)
    • newStatementHandler (controlador JDBC)
    • newResultSetHandler (manejador de conjunto de resultados)
    • newParameterHandler (manejador de parámetros)

¿Por qué usamos Configuración para crear Ejecutor aquí? Las razones son las siguientes:

1. Crear según el tipo de configuración

2. Ya sea para habilitar el caché

3. Usa interceptorChain para introducir complementos

Esto logra un empaquetado unificado y una creación estandarizada de componentes.
inserte la descripción de la imagen aquí

Lo mismo es cierto para la creación de varios otros componentes:

inserte la descripción de la imagen aquí

Hablemos de algunos componentes:

mapeadorRegistro

Se utiliza para registrar la interfaz del mapeador y generar su objeto proxy dinámico.
cachés

Caché, sesión cruzada a nivel de aplicación, todos los cachés se colocarán en Configuración después de la carga. Entonces mappedStatement hará una operación de asociación con nuestro caché, y hay una relación 1:1 entre ellos.

TipoAliasRegistro

TypeAliasRegistry es un registro para almacenar alias de tipo en MyBatis.
En MyBatis podemos utilizar <typeAlias>etiquetas para configurar alias para tipos Java, como por ejemplo:

<typeAlias type="com.someapp.model.User" alias="User"/>

Esto hará que User sea un alias del tipo com.someapp.model.User, y User se puede usar directamente para representar ese tipo en archivos de asignación posteriores.

Entonces, ¿cómo implementa MyBatis esta función de tipo alias?

Esto depende de TypeAliasRegistry

  • Todas las configuraciones de alias de tipo se guardan en TypeAliasRegistry. Cuando se analiza nuestro archivo de mapeo SQL, MyBatis buscará la <typeAlias>configuración y registrará el alias de tipo encontrado en el registro.
  • Luego, en el proceso de análisis posterior, siempre que MyBatis encuentre un tipo, primero verificará si TypeAliasRegistry tiene un alias para el tipo y, si existe, usará el alias, de lo contrario, usará el nombre completo.

Por lo tanto, la función de TypeAliasRegistry es guardar la configuración de alias de tipo y permitir que MyBatis encuentre y use estos alias cuando sea necesario . Implementa la función de alias de tipo de MyBatis.

Como uno de los componentes centrales de MyBatis, TypeAliasRegistry tiene principalmente las siguientes funciones:

  1. Guardar configuración de alias de tipo: MyBatis puede registrar el alias de tipo analizado desde el archivo de mapeo en él para su posterior búsqueda y uso.
  2. Encuentra un alias: Durante el proceso de análisis de MyBatis, siempre que se necesite un tipo, MyBatis primero buscará el alias del tipo en el TypeAliasRegistry. Si está presente, use el alias; de lo contrario, use el nombre completo.
  3. Configuración simplificada: Mediante alias de tipo se puede simplificar la configuración de mapeo de MyBatis. No necesitamos ingresar su nombre calificado completo cada vez que usamos un tipo, solo necesitamos usar el alias configurado por MyBatis, lo que mejora la legibilidad y la eficiencia de la configuración.
  4. Reducir el número de análisis: cada vez que MyBatis usa un alias en lugar de un nombre completo, puede reducir el análisis de ese paquete una vez, lo que puede mejorar ligeramente el rendimiento de análisis de MyBatis.

elemento de configuración

La información de configuración de configuración proviene de xml y anotaciones. Cada archivo y anotación se compone de varios elementos de configuración y presenta una relación anidada. La relación general se muestra en la siguiente figura:inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Para el uso de cada configuración, consulte la documentación proporcionada en el sitio web oficial: https://mybatis.org/mybatis-3/zh/configuration.html#properties
inserte la descripción de la imagen aquí

portador del elemento

Ya sea XML o mis anotaciones, estos elementos de configuración deben convertirse en atributos de configuración JAVA o componentes de objetos para transportarlos. La relación correspondiente es la siguiente:

  • La configuración completa (config.xml) la lleva la propiedad del objeto Configuración
  • el mapeo sql <select|insert...>o @Selectetc. es realizado por el objeto MappedStatement
  • Caché <cache..>o @CacheNamespacealojado por un objeto Caché
  • El mapa del conjunto de resultados está alojado en el objeto ResultMap.
    inserte la descripción de la imagen aquí

Análisis de archivos de configuración

Proceso de análisis de archivos XML

El análisis del archivo de configuración requiere que lo analicemos por separado, primero para analizar el proceso de análisis XML. La capa inferior del análisis de configuración xml utiliza dom4j para analizar primero en un árbol de nodos y, a continuación, hace coincidir diferentes analizadores según los diferentes tipos de nodos. Finalmente analizado en componentes específicos.

La clase base del analizador es BaseBuilder, que contiene un objeto de configuración global. El propósito de esto es que todos los componentes que se analizarán deben atribuirse colectivamente a la configuración. Echemos un vistazo a lo que hace cada analizador:

  • XMLConfigBuilder: analizar el archivo config.xml creará directamente un objeto de configuración para analizar la configuración global.
  • XMLMapperBuilder: archivo Parse Mapper.xml, contenido que contiene, etc.
  • MapperBuilderAssistant: Asistente de análisis Mapper.xml En un Mapper.xml, Cache se comparte con Statement (instrucción sql) y la distribución de componentes compartidos se realiza mediante este análisis.
  • XMLStatementBuilder: el análisis de asignación de SQL significa que los elementos <select|update|insert|delete> se analizan en MapperStatement.
  • SqlSourceBuilder: análisis de fuente de datos Sql, que analiza el SQL declarado en SQL ejecutable.
  • XMLScriptBuilder: analice el conjunto de scripts de SqlNode establecidos en el origen de datos de SQL dinámico.

inserte la descripción de la imagen aquí

El proceso de análisis general comienza en XmlConfigBuilder y luego se analiza gradualmente hacia adentro hasta que se analizan todos los nodos. Podemos entender su proceso de análisis general a través de un proceso de análisis de MappedStatement.

inserte la descripción de la imagen aquí

Descripción del flujo:

  • 【XmlConfigBuilder】 Reciba un flujo de entrada config.xml y luego cree un objeto de configuración vacío
    inserte la descripción de la imagen aquí

  • [XmlConfigBuilder] Analizar la configuración global
    inserte la descripción de la imagen aquí

  • [XmlConfigBuilder] análisis mapperElements, especifique el archivo mapper.xml a través de Resource o url

    • 【XmlMapperBuilder】Configuración pública como caché de análisis y configuración de conjunto de resultados
    • [XmlMapperBuilder] Asignación Parse Sql <seleccionar|insertar|actualizar|eliminar>
      • [XMLScriptBuilder] Analizar y generar fuentes de datos SQL, incluidos scripts dinámicos
    • [XmlMapperBuilder] Declaración de compilación
      • [MapperBuilderAssistant] Configure el caché y agréguelo a Configuración

Análisis de configuración de anotaciones

La implementación subyacente del análisis de anotaciones se logra mediante la obtención de elementos de anotación en la interfaz de Mapper a través de la reflexión. Hay dos maneras:

  • Una es especificar directamente el nombre de la interfaz.
  • Una es especificar el nombre del paquete y luego escanear automáticamente todas las clases de interfaz bajo el paquete.

Estas lógicas son implementadas por Mapper Registry (MapperRegistry). Recibe un parámetro de clase de interfaz y crea una fábrica de proxy dinámica para la interfaz basada en el parámetro, luego analiza las anotaciones del método interno para generar cada MapperStatement y finalmente lo agrega a Configuración para completar el análisis.

inserte la descripción de la imagen aquí

Si desea obtener más información, consulte el artículo:
Descripción general de la configuración principal de MyBatis-Configuración Explicación detallada de la
configuración principal de MyBatis—Configuración

Supongo que te gusta

Origin blog.csdn.net/zyb18507175502/article/details/131105326
Recomendado
Clasificación