【Mybatis 】- 配置初始化過程

Mybatis 配置初始化過程


測試代碼

SqlMapConfig.xmljava

<?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>
    <properties resource="db.properties"/>
    <typeAliases>
        <package name="com.zhiwei.entity"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="advanced/StudentMapper.xml"/>
    </mappers>
</configuration>

StudentMapper.xmlnode

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.zhiwei.advanced.mapper.StudentMapper">
<resultMap type="com.zhiwei.entity.Student" id="studentResultMap">
    <id column="sid" property="sid"/>
    <result column="stuname" property="stuName"/>
    <result column="stupasswd" property="stuPasswd"/>
    <result column="stuage" property="stuAge"/>
    <result column="stugender" property="stuGender" javaType="java.lang.String"/>
    <result column="stuscore" property="stuScore" javaType="java.lang.Integer"/>
</resultMap>

<resultMap type="com.zhiwei.advanced.pojo.StudentCustomer" id="studentCustomerResultMap">
    <id column="sid" property="sid"/>
    <result column="sid" property="uid"/>
    <result column="stupasswd" property="passwd"/>
    <result column="stuname" property="stuName"/>
    <result column="stupasswd" property="stuPasswd"/>
    <result column="stuage" property="stuAge"/>
    <result column="stugender" property="stuGender" javaType="java.lang.String"/>
    <result column="stuscore" property="stuScore" javaType="java.lang.Integer"/>
</resultMap>

<select id="findStudentById" parameterType="int" resultMap="studentResultMap">
	select * from student where sid = #{sid}
</select>
</mapper>

Studentsql

package com.zhiwei.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student {

    private int sid;
    private String stuName;
    private String stuPasswd;
    private int stuAge;
    private String stuGender;
    private float stuScore;
}

StudentMapper數據庫

package com.zhiwei.advanced.mapper;

import com.zhiwei.advanced.pojo.StudentCustomer;
import com.zhiwei.advanced.pojo.StudentQueryVo;
import com.zhiwei.entity.Student;
import java.util.List;

public interface StudentMapper {

    public Student findStudentById(Integer sid);
}

啓動類apache

sqlSession.getMapper(StudentMapper.class).findStudentById(id);

工做流程

解析XML映射爲org.apache.ibatis.session.Configuration對象,並以此爲基礎建立SessionFactory, SessionFactory建立的Session與Executor綁定,交由Executor執行緩存


配置加載過程

SessionFactory構建代碼session

Inputstream is = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SessionFactory構建

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)mybatis

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

XMLConfigBuilder 工具類:主要解析SqlMapConfig.xmlapp

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

new Configuration(): 默認填充配置:相似緩存別名映射類ide

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

XML解析:parser.parse()

parsed解析標誌,底層調用 parseConfiguration(parser.evalNode("/configuration")); 按照Xpath語法解析SqlMapConfig.xml根節點

private void parseConfiguration(XNode root) {
    try {
    
     // 解析SqlMapConfig.xml的properties標籤, configuration.setVariables(defaults)解析properties文件屬性到configuration緩存
      propertiesElement(root.evalNode("properties"));
      
      // setting配置: configuration.setVfsImpl(vfsImpl)
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);

      //別名配置:configuration.typeAliasRegistry
      //dtd約束:mybatis-3-config.dtd
      //package: key:類簡單名駝峯命名法,class:類全限定名
      //typeAlias: key: alias自定義別名 type:類全限定名
      //注意:最好不用直接引用package形式,不然多個包存在相同名的類會出現衝突
      typeAliasesElement(root.evalNode("typeAliases"));

      //插件配置:至關於攔截器可自定義操做,例如分頁、CRUD操做攔截,相似AOP:configuration.interceptorChain
      pluginElement(root.evalNode("plugins"));

      //設置對象工廠:哦、configuration.objectFactory
      objectFactoryElement(root.evalNode("objectFactory"));

      //社會組obectMapper工廠:configuration.objectWrapperFactory
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      //設置反射器工廠:configuration.reflectorFactory,反射機制操做
      reflectorFactoryElement(root.evalNode("reflectorFactory"));

      //mybatis全局配置:若無自定義則使用默認配置
      settingsElement(settings);

      //設置環境:configuration.environment,本質緩存當前工做數據庫信息
      //注意:Environment可設置多套數據庫環境,經過default屬性指定具體數據庫
      environmentsElement(root.evalNode("environments"));

      //明確指定mybatis工做的數據庫ID:數據庫產品名稱
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));

      //類型處理器:解析具體的Mapper數據類型: configuration.typeHandlerRegistry
      //做用:數據庫操做Mapper的解析和轉換工做
      typeHandlerElement(root.evalNode("typeHandlers"));

      //mapper處理:configuration.mapperRegistry
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

Mapper標籤解析

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {

        //package形式批量配置:經常使用
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        
        //mapper單個獨立配置
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            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) {
            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.");
          }
        }
      }
    }
  }

mapperParser.parse(); 解析

//解析流程
public void parse() {

    //判斷StudentMapper.xml是否已加載
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    //XML解析可能出現問題,重複處理,例如子ResultMap先於父ResultMap加載
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

configurationElement(parser.evalNode("/mapper")); 解析Mapper標籤

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標籤:configuration.parameterMaps
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));

     //解析resultMap標籤:configuration.resultMaps
      resultMapElements(context.evalNodes("/mapper/resultMap"));

      //解析sql標籤:configuration.sqlFragments
      sqlElement(context.evalNodes("/mapper/sql"));

      //解析:CRUD標籤映射爲MapperStatement
      //分析:構建sql原材料ParameterMaps、ResuldMap、sql片斷、namespace準備後開始構建
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

構建MapperStatement

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

statementParser.parseStatementNode(); 解析成MapperStatement

public void parseStatementNode() {
   String id = context.getStringAttribute("id");
   String databaseId = context.getStringAttribute("databaseId");

   if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
     return;
   }

   Integer fetchSize = context.getIntAttribute("fetchSize");
   Integer timeout = context.getIntAttribute("timeout");
   String parameterMap = context.getStringAttribute("parameterMap");
   String parameterType = context.getStringAttribute("parameterType");
   Class<?> parameterTypeClass = resolveClass(parameterType);
   String resultMap = context.getStringAttribute("resultMap");
   String resultType = context.getStringAttribute("resultType");
   String lang = context.getStringAttribute("lang");
   LanguageDriver langDriver = getLanguageDriver(lang);

   Class<?> resultTypeClass = resolveClass(resultType);
   String resultSetType = context.getStringAttribute("resultSetType");
   StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
   ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

   String nodeName = context.getNode().getNodeName();
   SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
   boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
   boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
   boolean useCache = context.getBooleanAttribute("useCache", isSelect);
   boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

   //填充include refid引用sql片斷
   XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
   includeParser.applyIncludes(context.getNode());

   //解析mybatis selectkey主鍵生成策略標籤
   processSelectKeyNodes(id, parameterTypeClass, langDriver);
   
   // 解析sql
   SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
   String resultSets = context.getStringAttribute("resultSets");
   String keyProperty = context.getStringAttribute("keyProperty");
   String keyColumn = context.getStringAttribute("keyColumn");
   KeyGenerator keyGenerator;

   //MapperStatementId生成策略:名稱空間 + id + !操做類型key
   String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
   keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
   if (configuration.hasKeyGenerator(keyStatementId)) {
     keyGenerator = configuration.getKeyGenerator(keyStatementId);
   } else {
     keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
         configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
         ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
   }

   builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
       fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
       resultSetTypeEnum, flushCache, useCache, resultOrdered, 
       keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
 }

configuration添加MapperStatement

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

綁定命名空間: bindMapperForNamespace();

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace

本質:命名空間和Mapper接口綁定,方便後續經過Mapper操做動態生成代理類

private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);

          //mapperRegistry:保存代理工廠類
          configuration.addMapper(boundType);
        }
      }
    }
  }

至此MapperStatement初始化工做完成

相關文章
相關標籤/搜索