【Mybatis 】- 配置初始化过程

Mybatis 配置初始化过程


测试代码

SqlMapConfig.xml

<?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.xml

<?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>

Student

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

启动类

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

工作流程

解析XML映射为org.apache.ibatis.session.Configuration对象,并以此为基础创建SessionFactory, SessionFactory创建的Session与Executor绑定,交由Executor执行


配置加载过程

SessionFactory构建代码

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)

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

XMLConfigBuilder 工具类:主要解析SqlMapConfig.xml

 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(): 默认填充配置:类似缓存别名映射类

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初始化工作完成

转载于:https://my.oschina.net/yangzhiwei256/blog/3031246

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值