mybatis-启动源码分析

Posted PacosonSWJTU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis-启动源码分析相关的知识,希望对你有一定的参考价值。

【1】测试用例

mybatis-config.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>
	<!-- 
		1)properteis
		1.1、mybatis 可以使用properties 标签来引入外部properties配置文件的内容;
		1.2、resource 引入类路径下 的资源;
		1.3、url 是引入网络路径或磁盘路径下的资源;
	 -->
	<properties resource="dbconfig.properties"></properties>
	
	<!-- 
		2)settings 包含很多重要的设置项
		2.1、setting 用来设置每一个设置项:
			name: 设置项名称, value:设置项取值;
			
	 -->
	 <!-- 开启驼峰命名规则,数据库表字段 a_b 可以解析为 类成员变量 aB -->
	 <!-- 显示指定每个需要更改的配置项,即使有默认值,防止版本更新带来的问题 -->
	 <settings>
	 	<!-- 开启驼峰命名法 -->
	 	<setting name="mapUnderscoreToCamelCase" value="false"/>
	 	<!-- 懒加载 -->
	 	<setting name="lazyLoadingEnabled" value="true"/>
	 	<!-- 侵入型懒加载 -->
	 	<setting name="aggressiveLazyLoading" value="false"/>
	 </settings>
	 
	 <!-- 
	 	3)别名处理器:可以为java类型起别名(注意:别名不区分大小写)
	 	3.1) typeAlias为某一个java类型取别名, type:指定要起别名的类型全类名,默认别名就是类名小写,employee
	 				alias: 指定新的 别名;
	 	3.3)
	  -->
	 <typeAliases>
	 	<!-- 
	 		3.1) typeAlias为某一个java类型取别名, type:指定要起别名的类型全类名,默认别名就是类名小写,employee
	 				alias: 指定新的 别名;
	 	 
	 	<typeAlias type="com.swjtu.mybatis.bean.Employee" alias="emp"/>
	 	-->
	 	<!-- 
	 		3.2)package: 为某个包下的所有类批量起别名:
	 			name: 指定包名,为当前包以及所有的子包的每个类都起一个默认别名(类名小写);
	 	-->
	 	<!-- 
	 		3.3) 批量起别名的情况下,可以使用注解 Alias()为某个类型指定新的别名;
	 			  存在 a 包 和 a.b 包下都有 Employee类的情况;
	 	 -->
	 	<package name="com.swjtu.mybatis.bean"/>
	 </typeAliases>
	 
	 <!-- 
	 	4、environments: 环境们, mybatis可以配置多种环境, default指定使用某种环境。可以达到快速切换环境。
	 		4.1)environment:配置一个具体的环境信息;必须有两个标签:id代表当前环境的唯一标识
	 			4.1.1) transactionManager 必须有, 事务管理器,
	 				type 事务管理器类型,type="[JDBC|MANAGED]" 
	 				          自定义事务管理器:实现 TransactionFactory 接口, type指定全类名 
	 				Configuration类中定义了别名: org.apache.ibatis.session.Configuration
		 				 typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
	    				 typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
	 			
	 			4.1.2) dataSource 数据源:
	 				   	type:数据源类型 type="[UNPOOLED|POOLED|JNDI]"
	 				   			  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
								    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
								    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
						自定义数据源: 实现 DataSourceFactory 接口 , type 为自定义数据源的全类名 			 				   			  
	  -->					  	
	<environments default="development">
		<!-- 测试环境 -->
		<environment id="test">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
		
		<!-- 开发环境 -->
		<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.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 
		5、databaseIdProvider :支持多数据库厂商的;
			type=DB_VENDOR: VendorDatabaseIdProvider
			作用就是得到数据库厂商标识(由数据库驱动自己附带的 getDatabaseProductName),mybatis根据该标识执行不同的sql;
			并在 sql 标签如<select> 添加属性 databaseId 属性
	--> 
	<databaseIdProvider type="DB_VENDOR">
		<!-- 为不同数据库厂商起别名 -->
		<property name="mysql" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider> 
	
	<!-- 
		将我们写好的sql 映射文件 EmployeeMapper.xml 一定要注册到全局配置文件 mybatis-config.xml 中;
		6、mappers:将sql映射注册到全局配置中;
	 --> 
	<mappers>
		<!-- 
			mapper标签: 注册一个sql映射
				注册配置文件的两种方法:
					方法1:resource: 引用类路径下的sql 映射文件;
					方法2:url: 引用网络路径或磁盘路径下的 sql 映射文件;
				注册接口的方法:
					方法1 class: 直接引用(注册)接口, 写接口的全类名;
						补充1:  有sql映射文件,映射文件名必须和接口同名,并且仿造同一个目录下;
						补充2: 没有sql映射文件, 所有sql 利用注解修饰接口上;
						补充3: 不推荐使用注解,而使用sql映射文件的方式来 进行sql 映射。
						
		 -->
		 
		<mapper resource="com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml"/> 
		<!-- 
			批量注册: 前提是要把 Mapper类文件和Mapper sql 映射文件放在同目录下
			荔枝:<package name="com.swjtu.mybatis.dao"/> 可以吧 该包下的所有mapper 映射文件注册到全局配置环境中;
		 -->
		 <!-- <mapper class="com.swjtu.mybatis.dao.cwn.MYCwnOprDAO"/> -->
	</mappers>
	
	
	
	
	
	
	
	
	
	
</configuration>

执行sql;

public static void main1() {
		/*1. 获取 SqlSessionFactory 对象*/
		SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
		
		/*2.获取SqlSession对象, 不会自动提交数据*/
		SqlSession session = sqlSessionFactory.openSession();
		try { 
			String flag = "0911A"; 
			
			List<Map<String, Object>> list = new ArrayList<>();
			for (int i = 0; i < 5; i++) {  
				String indexFormat = String.format("%8d", i);
				Map<String, Object> map = new HashMap<>(); 
				map.put("RCRD_ID", flag + indexFormat); 
				map.put("CUST_NUM", "CUST_NUM" +flag+ indexFormat);
				map.put("CUST_NAME", "CUST_NAME" +flag+ indexFormat);
				map.put("WARN_TIMES", i); 
				list.add(map); 
			} 
//			mapper.insert2CustWarn(list);
			session.insert("com.swjtu.mybatis.dao.EmployeeMapper.insert2CustWarn", list); 
			session.commit();
		} finally { 
			session.close();
		}
		System.out.println("bingo!");
	} 
mapper文件中的 dml 元素
<insert id="insert2CustWarn">
		INSERT INTO my_cust_warn_tbl
               (
                rcrd_id
                , cust_num
                , cust_name 
                , warn_times
               ) VALUES 
                 <foreach collection="list" item="item" separator=",">
                 	(
                 		#{item.RCRD_ID}
                 		, #{item.CUST_NUM}
                 		, #{item.CUST_NAME}
                 		, #{item.WARN_TIMES}
                 	)
                 </foreach>
	</insert> 

MyBatisUtils.getSqlSessionFactory();

/**
 * 获取SqlSessionFactory 
 * @return
 */
public final static SqlSessionFactory getSqlSessionFactory() {
	String resource = "mybatis-config.xml";
	InputStream inputStream;
	try { 
		inputStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		return sqlSessionFactory;
	} catch (IOException e) {
		e.printStackTrace();
	}
	return null;
}


【2】创建 SqlSessionFactory 对象

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

实际上返回的是  DefaultSqlSessionFactory;

// SqlSessionFactoryBuilder
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.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

 而 build(Configuration config) 中的 config 是从mybatis-config.xml文件解析而来的;


【2.1】XMLConfigBuilder.parse() 产生Configuration

parser.parse() 方法的逻辑如下(XMLConfigBuilder构造器中新建了 Configuration对象):

// SqlSessionFactoryBuilder 
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());// 新建了一个 configuration对象
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));// 解析xml节点
    return configuration;
  }

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

我们跟踪了 mapperElement(..)  方法,它是解析 mybatis-config.xml 文件中在 mapper标签中配置的resource属性指定的sql mapper文件,比如

<mappers>				 
    <mapper resource="com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml"/>     
    <mapper class="com.swjtu.mybatis.dao.EmployeeMapperAnnotation"/>			
</mappers>

 其中 root.evalNode("mappers") 获取的值就是 mapper 数组; 

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) {
            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.");
          }
        }
      }
    }
  }

Xnode parent 长这样(就是 mybatis-config.xml 中 mappers元素内容):

以上代码,我们发现,解析 mapper有三种方式;

  • 1.通过resource 配置;
  • 2.通过url;
  • 3.通过mapper class;

下面,我们以resource 为例来讲解;

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

以上代码发现,每一个mapper文件,都会创建一个 XMLMapperBuilder 对象来解析它

String resource=“com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml”;

XMLMapperBuilder构造器传入了4个参数

  1.  mapper文件的输入流(com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml文件流);
  2. mybatis配置对象Configuration;
  3. resource(sql文件名称com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml);
  4. sqlFragments-sql片段为空;

XMLMapperBuilder构造器;该构造器新建了 MapperBuilderAssistant对象(解析mapper文件的助手)

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }

  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

MapperBuilderAssistant() 长这个样子;


 【2.2】XmlMapperBuilder().parse()  解析mappers下的每个mapper文件内容

接着,进入  XmlMapperBuilder().parse() 方法,解析mappers下的mapper元素的内容,mapper元素如     <mapper resource="com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml"/>    ;如下:

代码步骤:

  • step1)解析mapper文件下 mapper元素内容(主要是 dml标签元素内容);
  • step2)把mapper文件名添加到 configuration,以避免二次解析;
  • step3)构建命名空间;

【2.2.1】 XmlMapperBuilder.configurationElement(parser.evalNode("/mapper"))如下: 

其中 parser.evalNode("/mapper")或context就是 文件 com\\swjtu\\mybatis\\dao\\EmployeeMapper.xml  的内容,如下:

代码步骤:

  • step1)设置命名空间;
  • step2)解析缓存;
  • step3)解析 parameterMap 标签内容;
  • step4)解析 resultMap 标签内容;
  • step5)解析 sql 标签内容;
  • step6) 解析 sql 的dml 标签内容(select或insert或update 或 delete);

最后一步, buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 就是真正解析 sql的 dml语句的标签了,即解析出sql文本并加载到内存中; 

List<XNode>  list 包含了多个 dml标签的内容数组;

接着 buildStatementFromContext(List<XNode> list, String requiredDatabaseId) 遍历list中的每个dml 标签元素;  如下:

private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

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

以上代码,每个 dml标签元素 就会创建一个 XMLStatementBuilder 对象

statementParser.parseStatementNode(); 

就用于解析 context 文本(dml标签元素内容); 如下:

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 Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    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))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

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

以上代码的步骤如下:

step1)解析 dml元素中各种属性,包括 parameterMap,parameterType,resultMap,resultType等;

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

resultType 为map;因为 dml标签元素如下:

<select id="getEmpByLastNameLikeReturnMap" resultType="map">
 		    SELECT id AS ID
		       , last_name AS LASTNAME
		       , email AS EMAIL
		       , gender AS GENDER
		  FROM emp_tbl 
		 WHERE last_name like #{lastName}
 	</select>

step2)获取语言驱动器, LanguageDriver langDriver = getLanguageDriver(lang);

step3)    Class<?> resultTypeClass = resolveClass(resultType);:返回 resultType的class类型;如 resultType为map,则返回 Map.class ;

protected Class<?> resolveClass(String alias) { // 以alias为map为例
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
// BaseBuilder.java 
protected Class<?> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

typeAliasRegistry 为类型别名注册器,如下:

public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

step4)获取sql语句类型, PREPARED ;

step5)获取sql命令类型 SqlCommandType -SELECT

String nodeName = context.getNode().getNodeName(); // nodeName为select

SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

public enum SqlCommandType {
  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

step6)解析是否需要使用缓存

boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);//false
boolean useCache = context.getBooleanAttribute("useCache", isSelect);//true
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);//false

step7)解析dml元素中 include元素内容(比如 include sql);

 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

public void applyIncludes(Node source) {
    Properties variablesContext = new Properties();
    Properties configurationVariables = configuration.getVariables();
    if (configurationVariables != null) {
      variablesContext.putAll(configurationVariables);
    }
    applyIncludes(source, variablesContext);
  }

 step8)解析 selectKey;sql中没有,不用考虑

processSelectKeyNodes(id, parameterTypeClass, langDriver);

 step9)创建sql 映射对象

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

langDriver 指向了 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

public class XMLLanguageDriver implements LanguageDriver {

  @Override
  public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  }

  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }

  @Override
  public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    // issue #3
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
      return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
    } else {
      // issue #127
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);
      if (textSqlNode.isDynamic()) {
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
  }

}

builder.parseScriptNode()  调用的是 XMLScriptBuilder.parseScriptNode() ; 解析动态sql (用${} 包裹的sql );

 public SqlSource parseScriptNode() {
    List<SqlNode> contents = parseDynamicTags(context);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

  List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }

因为我们不是动态sql,所以返回的是 RawSqlSource

sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);

step10)解析 resultSets, keyProperty, keyColumn 属性等 (也不用太关心); 

String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    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))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

step11)MapperBuilderAssistant.addMappedStatement() 把解析得到的sql映射对象添加到configuration;

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

参数列表如下:

  • 1.id,getEmpByLastNameLikeReturnMap;
  • 2.sqlSource,

 parameterMappins 明显是 参数映射;

  • 3.statementType , PREPARED;
  • 4.sqlCommandType,SELECT;
  • 5.fetchSize,null;
  • 6.timeout, null;
  • 7.parameterMap,null;
  • 8.parameterTypeClass,null;
  • 9.resultMap,null;
  • 10.resultTypeClass, interface java.util.Map;
  • 11.resultSetTypeEnum,null;
  • 12.flushCache,false;
  • 13.useCache,true;
  • 14.resultOrdered,false;
  • 15.keyGenerator,org.apache.ibatis.executor.keygen.NoKeyGenerator@5ef8df1e;
  • 16.keyProperty,null;
  • 17.keyColumn,null;
  • 18.databaseId,null;
  • 19.langDriver, org.apache.ibatis.scripting.xmltags.XMLLanguageDriver@32057e6;
  • 20.resultSets, null;

MapperBuilderAssistant.addMappedStatement(...)  源码如下:

public MappedStatement addMappedStatement(
      ...) {

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

id = applyCurrentNamespace(id, false); 这一步,给id添加命名空间前缀,

  • 执行前;id为  getEmpByLastNameLikeReturnMap ;
  • 执行后,id为 com.swjtu.mybatis.dao.EmployeeMapper.getEmpByLastNameLikeReturnMap;

解析每个dml元素时,都要创建一个  MappedStatement.Builder(...) 用于创建 MappedStatement( sql映射信息)

MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)

MappedStatement statement = statementBuilder.build(); 源码如下:

public MappedStatement build() {
      assert mappedStatement.configuration != null;
      assert mappedStatement.id != null;
      assert mappedStatement.sqlSource != null;
      assert mappedStatement.lang != null;
      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
      return mappedStatement;
    }


补充:MappedStatement对象

MappedStatement 实际是封装了 sql映射信息的对象 ,sql映射是在 MapperBuilderAssistant.addMappedStatement(...) 方法里面构建;结构如下:

每条sql都是一个 MappedStatement对象,解析完成后,将其添加到 configuration对象中;

 configuration 就是根据mybatis-config.xml 文件解析出的 Configuration 对象;长这个样子:


【2.2.2】 configuration.addLoadedResource(resource);

把已经解析的resource 文件名添加到 configuration,避免二次解析;


【2.2.3】XMLMapperBuilder.bindMapperForNamespace() 创建命名空间

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);
          configuration.addMapper(boundType);
        }
      }
    }
  }
 public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

很显然,当 有mapperclass的时候,才会设置 boundType;如果没有mapper class也没有关系;因为catch会忽略异常;


【2.2.4】解析其他

 parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();

【2.2.5】创建 DefaultSqlSession对象并返回

解析完成后,生成Configuration对象;


【3】sqlSessionFactory.openSession()打开会话

调用的是 DefaultSqlSessionFactory.openSession()

// DefaultSqlSessionFactory 
@Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

 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);
      // 返回的是  DefaultSqlSession 
      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();
    }
  }


【3.1】DefaultSqlSessionFactory 中有两类方法包括,

  1. 从数据源获取会话:private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit);
  2. 从连接中获取会话:private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) ;

两者不同点在于

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

final Transaction tx = transactionFactory.newTransaction(connection);

// 从数据源获取会话 
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);// 执行器 
      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();
    }
  }

// 从数据库连接获取会话 
  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }      
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

获取会话无论采用 datasource,还是connection方式,其步骤如下:

  • step1)获取环境对象;
  • step2)获取事务工厂;
  • step3)创建事务;
  • step4)创建执行器;
  • step5)创建会话 DefaultSqlSession;

【3.2】事务工厂

// 从环境对象中获取事务工厂
 private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

如果配置了环境元素,则返回配置的环境的事务工厂;否则默认返回 ManagedTransactionFactory;

此外,事务工厂有3个,如下:


【3.3】创建事务-ManagedTransaction

// 可管理的事务工厂
public class ManagedTransactionFactory implements TransactionFactory {

  private boolean closeConnection = true;

  @Override
  public void setProperties(Properties props) {
    if (props != null) {
      String closeConnectionProperty = props.getProperty("closeConnection");
      if (closeConnectionProperty != null) {
        closeConnection = Boolean.valueOf(closeConnectionProperty);
      }
    }
  }

  @Override
  public Transaction newTransaction(Connection conn) {
    return new ManagedTransaction(conn, closeConnection);
  }
// 创建事务对象
  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    // Silently ignores autocommit and isolation level, as managed transactions are entirely
    // controlled by an external manager.  It's silently ignored so that
    // code remains portable between managed and unmanaged configurations.
    return new ManagedTransaction(ds, level, closeConnection);
  }
}

可管理事务: ManagedTransaction; 

public class ManagedTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(ManagedTransaction.class);

  private DataSource dataSource;
  private TransactionIsolationLevel level;
  private Connection connection;
  private boolean closeConnection;

  public ManagedTransaction(Connection connection, boolean closeConnection) {
    this.connection = connection;
    this.closeConnection = closeConnection;
  }

  public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
    this.dataSource = ds;
    this.level = level;
    this.closeConnection = closeConnection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

  @Override
  public void close() throws SQLException {
    if (this.closeConnection && this.connection != null) {
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + this.connection + "]");
      }
      this.connection.close();
    }
  }

  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    this.connection = this.dataSource.getConnection();
    if (this.level != null) {
      this.connection.setTransactionIsolation(this.level.getLevel());
    }
  }

}

 它的commit, rollback 都是空的;

它的数据库连接的获取有两种方式,

  1. 上游给定,传入 Connection ;
  2. 通过 datasource 数据源获取connection;

【3.4】创建执行器

1)ExecutorType 是枚举类型,默认为SIMPLE ;

final Executor executor = configuration.newExecutor(tx, execType);

// Configuration.newExecutor 方法
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;
  }

如果为SIMPLE,则返回 SimpleExecutor 执行器;  但 如果启用缓存,则封装SimpleExecutor到缓存执行器

最后把 执行器添加到拦截器列表,拦截器 Interceptor 列表如下,本质上是一个 ArrayList数组;

// 拦截器
public class InterceptorChain {
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

其中SimpleExecutor父类Executor的实现类 包括

2)SimpleExecutor 长这个样子

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>queryCursor(stmt);
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

}

【3.5】 创建会话 DefaultSqlSession

 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

【4】session.insert(...)

【4.1】DefaultSqlSession.insert() 

session.insert(...); 它实际调用的是 DefaultSqlSession.insert() 方法;

// DefaultSqlSession 
@Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }

  @Override
  public int update(String statement) {
    return update(statement, null);
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 executor.update(ms, warpCollection(parameter)); 中的  executor是 SimpleExecutor;

我们接着看下 SimpleExecutor-执行器 ;

// 简单执行器 
public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
  
// 执行器基类 
public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  protected Transaction transaction;
  protected Executor wrapper;

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;

  protected int queryStack = 0;
  private boolean closed;

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }

BaseExecutor.update(MappedStatement ms, Object parameter) 如下:

@Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

SimpleExecutor.doUpdate(...)

 @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

上述是执行sql 语句的地方,依赖的对象包括:

  1. configuration:根据mybatis-config.xml 解析得到的配置对象;
  2. StatementHandler:语句处理器;
  3. PreparedStatement:预编译语句;

Configuration.newStatementHandler(...) 方法如下:

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

参数列表为:

参数1-executor;

参数2-mappedStatement; 

参数3-parameterObject;

 

参数4- parameterObject 为:

{collection=[{RCRD_ID=0908A       0,
 CUST_NUM=CUST_NUM       0,
 WARN_TIMES=0,
 CUST_NAM

以上是关于mybatis-启动源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis之Mapper调用源码分析

MyBatis之启动分析

MyBatis 源码分析——配置信息

MyBatis源码分析select源码分析及小结

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段