mybatis-config.xml解析

入口

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

build方法

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  //构建sqlSessionFactory 返回默认实现
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

parse方法

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    /**
     * 3.构建Configuration
     * 找到configuration标签
     */
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parseConfiguration方法

private void parseConfiguration(XNode root) {
    try {
      //解析<properties resource="dbConfig.properties"></properties>节点
      propertiesElement(root.evalNode("properties"));
      //解析<settings></settings>节点
        //拿到所有的子节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
        //解析typeAliases节点
      typeAliasesElement(root.evalNode("typeAliases"));
        //解析plugins节点
      pluginElement(root.evalNode("plugins"));
        //解析objectFactory节点
      objectFactoryElement(root.evalNode("objectFactory"));
        //解析objectWrapperFactory节点
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        //解析reflectorFactory节点
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
        //解析environments节点
      environmentsElement(root.evalNode("environments"));
        //解析databaseIdProviders节点
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        //解析typeHandlers节点
      typeHandlerElement(root.evalNode("typeHandlers"));
        //解析mappers节点
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

Properties标签

private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    //获取settings下的子标签
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
      // 检查是不是所有的配置已经在Configuration中声明  <setting name="logImpl" value="LOG4J"/>
      //首先检查name属性是不是在Configuration类中进行了声明,方法中使用的反射技术进行的检查
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

typeAliases标签

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {//遍历子节点
          //子节点有两种类型 一种是 package标签,一种是typeAlias标签
        if ("package".equals(child.getName())) {
            /**
             * 为一个包下的所有类起别名,name为包名。别名和类名相同,省略包名
             <typeAliases>
             <package name="com.it.entity"></package>
             </typeAliases>
             */
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
            /**
             * 为一个类起别名,type为全类名,alias为别名
             <typeAliases>
             <typeAlias type="com.it.entity.Student" alias="Student"></typeAlias>
             </typeAliases>
             */
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {

            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
                //如果别名未设置 则查找是有@Alias注解 如果还没有 则使用java中类名作为key
              typeAliasRegistry.registerAlias(clazz);
            } else {
                //如果设置了别名 则直接使用别名作为key
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

plugin标签

 /**
     *  MyBatis 的插件实际上就是拦截器
     *  并且提供了可扩展的位置有4个
     *  Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
        ParameterHandler (getParameterObject, setParameters)
        ResultSetHandler (handleResultSets, handleOutputParameters)
        StatementHandler (prepare, parameterize, batch, update, query)
      注意:分页助手的插件 配置在mapper之前
     <plugin interceptor="com.github.pagehelper.PageHelper">
     指定方言
     <property name="dialect" value="mysql"/>
     * @param parent
     * @throws Exception
     */
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

objectFactory标签

/**
   * 对象工厂  用于创建和表对应的实体类对象
   * MyBatis 内置了一个 DefaultObjectFactory 来实现结果对象的创建,而我们定义的结果对象通常都有默认的无参构造器,
   * 或者有显式定义无参构造器,这样也是方便 DefaultObjectFactory 帮我们创建结果对象
   * @param context
   * @throws Exception
   */
  private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      //注册到mybatis中ExtendsObjectFactory 继承了DefaultObjectFactory 也可以实现 ObjectFactory
      //<objectFactory type="com.linkedbear.mybatis.factory.ExtendsObjectFactory"/>
      String type = context.getStringAttribute("type");
      Properties properties = context.getChildrenAsProperties();
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
      factory.setProperties(properties);
      configuration.setObjectFactory(factory);
    }
  }

environments标签

/**
   * 它类似于 SpringFramework 中的 profiles 的概念,它允许我们定义多个环境,以分别定义不同的数据库连接配置。
   * 这种场景在我们平时开发中是很常见的:开发、测试、生产分别要用到 3 种不同的环境,连接 3 个不同的数据库,这个时候就需要分别配置了。
   * 尽管我们可以定义多个环境配置,但激活生效的只允许选择一个,激活的方式是在 <environments> 标签上声明 default 属性。
   *  <environments default="development">
         <environment id="development">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 <property name="driver" value="${jdbc.driverClassName}"/>
                 <property name="url" value="${jdbc.url}"/>
                 <property name="username" value="${jdbc.username}"/>
                 <property name="password" value="${jdbc.password}"/>
             </dataSource>
         </environment>
         <environment id="production">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 <property name="driver" value="${prod.driverClassName}"/>
                 <property name="url" value="${prod.url}"/>
                 <property name="username" value="${prod.username}"/>
                 <property name="password" value="${prod.password}"/>
             </dataSource>
         </environment>
   </environments>
   * @param context
   * @throws Exception
   */
  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {//默认选择那个环境
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        //其他环境的唯一标识
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {//默认环境default值与其他环境的id值一样则进行事务和数据库连接信息的处理
          //事务工厂
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //数据源工厂
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

          //构建真正的环境
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

 /**
   * 数据库厂商标识
   * databaseIdProvider 数据库厂商标识,它为我们提供了数据库可移植性的支持。
   * 了解 Hibernate 的小伙伴应该知道,Hibernate 的数据库可移植性是天然的,
   * MyBatis 虽然不像 Hibernate 那样全自动,但它也提供了支持的方案:我们在编写 CmpRecordMapper.xml 时,
   * 针对不同的 statement ,声明不同的数据库厂商标识,MyBatis 即会动态的根据数据库厂商标识,使用不同的 statement ,
   * 从而达到不同的数据库发送不同 SQL 的效果。
   *     <databaseIdProvider type="DB_VENDOR"> DB_VENDOR是固定的
             <property name="MySQL" value="mysql"/>
             <property name="Oracle" value="oracle"/>
             <property name="PostgreSQL" value="postgresql"/>
         </databaseIdProvider>
   每个 statement 都可以声明 databaseId 属性,以指定不同的数据库厂商。
   <select id="findAllByDepartmentId" parameterType="string" resultType="com.linkedbear.mybatis.entity.User"
          databaseId="mysql">
           select * from tbl_user where department_id = #{departmentId}
   </select>

   <select id="findAllByDepartmentId" parameterType="string" resultType="com.linkedbear.mybatis.entity.User"
          databaseId="postgresql">
          注意这里查的表不一样
         select * from users where department_id = #{departmentId}
   </select>
   * @param context
   * @throws Exception
   */
  private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
          type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
  }

  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

typeHandlers标签

/**
   * 类型处理器 用于java数据类型与jdbc数据类型之间的转换
   *
   * 类型处理器	        javaType	    jdbcType
    IntegerTypeHandler	Integer 、int	int / numeric
    DoubleTypeHandler	Double 、double	double / numberic
    StringTypeHandler	String	        varchar / char
    DateTypeHandler	    Date	        timestamp
    EnumTypeHandler	    Enum	        varchar / char

   <typeHandlers>
       <typeHandler handler="com.linkedbear.mybatis.handler.DepartmentTypeHandler"
          javaType="com.linkedbear.mybatis.entity.Department" jdbcType="VARCHAR"/>
   </typeHandlers>
   <select id="findAllUseTypeHandler" resultMap="userHandlerMap">
       select * from tbl_user
   </select>
   <resultMap id="userHandlerMap" type="com.linkedbear.mybatis.entity.User">
       <id property="id" column="id"/>
       <result property="name" column="name"/>
       <result property="age" column="age"/>
       <result property="birthday" column="birthday"/>
       <association property="department" javaType="com.linkedbear.mybatis.entity.Department"/>
   </resultMap>
   * @param parent
   */
  private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

mappers标签

 /**
   * 映射器
   * <mappers>
         <mapper resource="mapper/department.xml"/> 直接加载mapper.xml
         <mapper class="com.linkedbear.mybatis.mapper.UserMapper"/> 加载Mapper接
         <package name="com.linkedbear.mybatis.mapper"/> 包扫描Mapper接口
     </mappers>
   * @param parent
   * @throws Exception
   */
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //包扫描 获取Mapper接口的包路径
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          //获取mapper.xml 配置文件的路径(一般再类路径(classpath))
          String resource = child.getStringAttribute("resource");
          //也可以指定网络mapper.xml的路径
          String url = child.getStringAttribute("url");
          //获取mapper接口
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {//从类路径下加载mapper.xml
            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) {//从网络上加载mapper.xml
            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) {//加载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.");
          }
        }
      }
    }
  }
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值