项目

mybatis配置文件mybatis.xml

mapper.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.geely.cep.aso.statement.dao.StatementDao"><resultMap id="BaseResultMap" type="com.geely.cep.aso.statement.model.SvcStatement"><id column="Id" jdbcType="BIGINT" property="id"/><result column="StatementNo" jdbcType="NVARCHAR" property="statementNo"/><result column="ServiceProxyCode" jdbcType="NVARCHAR" property="serviceProxyCode"/></resultMap><sql id="Base_Column_List">Id, StatementNo, ServiceProxyCode, SystemOrder, StatementType, DealerId, DealerName,NextMaintainMileage, NextMaintainAt, AccountType, StatementTime, StatementerId, StatementerName</sql><select id="selectStatementClmStatusByStatementNo" parameterType="String" resultType="Integer">SELECT clmstatusFROM SvcStatementWHERE statementNo = #{value}AND isnull(IsDeleted,0) = 0</select></mapper>

main函数

public class Main {public static void main(String[] args) throws IOException {String resource = "mybatis.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();try {ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);List<Product> productList = productMapper.selectProductList();for (Product product : productList) {System.out.printf(product.toString());}} finally {sqlSession.close();}}
}

初始化过程

1.读取配置文件

InputStream inputStream = Resources.getResourceAsStream(resource);

2.创建SqlSessionFactory对象

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

SqlSessionFactoryBuilder

  public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}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.}}}

2.1创建XMLConfigBuilder

XMLxxxBuilder是用来解析XML配置文件的,不同类型XMLxxxBuilder用来解析MyBatis配置文件的不同部位。比如:XMLConfigBuilder用来解析MyBatis的配置文件,XMLMapperBuilder用来解析MyBatis中的映射文件(如上文提到的ProductMapper.xml),XMLStatementBuilder用来解析映射文件中的SQL语句。
这些XMLxxxBuilder都有一个共同的父类——BaseBuilder。这个父类维护了一个全局的Configuration对象,MyBatis的配置文件解析后就以Configuration对象的形式存储。
当创建XMLConfigBuilder对象时,就会初始化Configuration对象,并且在初始化Configuration对象的时候,一些别名会被注册到Configuration的typeAliasRegistry容器中。

XMLConfigBuilder创建,先解析xml文件输入流成XPathParser,然后创建一个Configuration对象属性

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

Configuration初始化变量,创建很多对象属性。创建时,注册很多别名

  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();public Configuration() {typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);...}

TypeAliasRegistry创建时,注册很多基本类型的别名,存到hashmap中,

  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();public TypeAliasRegistry() {registerAlias("string", String.class);registerAlias("byte", Byte.class);}public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// issue #748String key = alias.toLowerCase(Locale.ENGLISH);if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");}TYPE_ALIASES.put(key, value);}

XMLConfigBuilder调用父类BaseBuilder的构造器,赋值Configuration 对象

  public BaseBuilder(Configuration configuration) {this.configuration = configuration;this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();}

2.2 XMLConfigBuilder解析xml配置文件,存储到Configuration 对象属性中

XMLConfigBuilder

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {// 解析<properties>节点propertiesElement(root.evalNode("properties"));// 解析<settings>节点Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);// 解析<typeAliases>节点typeAliasesElement(root.evalNode("typeAliases"));// 解析<plugins>节点pluginElement(root.evalNode("plugins"));// 解析<objectFactory>节点objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));// 解析<reflectorFactory>节点reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// 解析<environments>节点environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));// 解析<mappers>节点mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

XMLConfigBuilder会依次解析配置文件中的、< settings >、< environments>、< typeAliases >、< plugins >、< mappers >等属性。

2.2.1 节点的解析过程

<properties resource="org/mybatis/example/config.properties"><property name="username" value="dev_user"/><property name="password" value="F2Fa3!33TYyg"/>
</properties>
private void propertiesElement(XNode context) throws Exception {if (context != null) {// 获取<properties>节点的所有子节点Properties defaults = context.getChildrenAsProperties();// 获取<properties>节点上的resource属性String resource = context.getStringAttribute("resource");// 获取<properties>节点上的url属性String url = context.getStringAttribute("url");// resource和url不能同时存在if (resource != null && url != null) {throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");}if (resource != null) {// 获取resource属性值对应的properties文件中的键值对,并添加至defaults容器中        defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {// 获取url属性值对应的properties文件中的键值对,并添加至defaults容器中defaults.putAll(Resources.getUrlAsProperties(url));}// 获取configuration中原本的属性,并添加至defaults容器中Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}parser.setVariables(defaults);// 将defaults容器添加至configuration中configuration.setVariables(defaults);}
}

首先读取节点下的所有节点,并将每个节点的name和value属性存入Properties中。
然后读取节点上的resource、url属性,并获取指定配置文件中的name和value,也存入Properties中。(PS:由此可知,如果resource节点上定义的属性和properties文件中的属性重名,那么properties文件中的属性值会覆盖resource节点上定义的属性值。)
最终,携带所有属性的Properties对象会被存储在Configuration对象中。

节点的解析过程

<settings><setting name="cacheEnabled" value="true"/><setting name="lazyLoadingEnabled" value="true"/><setting name="multipleResultSetsEnabled" value="true"/>
</settings>

属性的解析过程

<typeAliases><typeAlias alias="Author" type="domain.blog.Author"/><typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>

MyBatis会为指定包下的所有类起一个别名,该别名为首字母小写的类名。

<typeAliases><package name="domain.blog"/>
</typeAliases>
private void typeAliasesElement(XNode parent) {if (parent != null) {// 遍历<typeAliases>下的所有子节点for (XNode child : parent.getChildren()) {// 若当前结点为<package>if ("package".equals(child.getName())) {// 获取<package>上的name属性(包名)String typeAliasPackage = child.getStringAttribute("name");// 为该包下的所有类起个别名,并注册进configuration的typeAliasRegistry中          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} // 如果当前结点为< typeAlias >else {// 获取alias和type属性String alias = child.getStringAttribute("alias");String type = child.getStringAttribute("type");// 注册进configuration的typeAliasRegistry中try {Class<?> clazz = Resources.classForName(type);if (alias == null) {typeAliasRegistry.registerAlias(clazz);} else {typeAliasRegistry.registerAlias(alias, clazz);}} catch (ClassNotFoundException e) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}
}

如果节点下定义了节点,那么MyBatis会给该包下的所有类起一个别名(以类名首字母小写作为别名)
如果节点下定义了节点,那么MyBatis就会给指定的类起指定的别名。
这些别名都会被存入configuration的typeAliasRegistry容器中

节点的解析过程

mappers有4种方式

<mappers><package name="org.mybatis.builder"/>
</mappers>
<mappers><mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
<mappers><mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<mappers><mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>

MyBatis会遍历下所有的子节点,如果当前遍历到的节点是,则MyBatis会将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中。
如果当前节点为,则会依次获取resource、url、class属性,解析映射文件,并将映射文件对应的Mapper Class注册到configuration的mapperRegistry容器中。

  private void mapperElement(XNode parent) throws Exception {if (parent != null) {// 遍历<mappers>下所有子节点for (XNode child : parent.getChildren()) {// 如果当前节点为<package>if ("package".equals(child.getName())) {// 获取<package>的name属性(该属性值为mapper class所在的包名)String mapperPackage = child.getStringAttribute("name");// 将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中configuration.addMappers(mapperPackage);} // 如果当前节点为<mapper>else {// 依次获取resource、url、class属性String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");// 解析resource属性(Mapper.xml文件的路径)if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);// 将Mapper.xml文件解析成输入流InputStream inputStream = Resources.getResourceAsStream(resource);// 使用XMLMapperBuilder解析Mapper.xml,并将Mapper Class注册进configuration对象的mapperRegistry容器中XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());mapperParser.parse();} // 解析url属性(Mapper.xml文件的路径)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();} // 解析class属性(Mapper Class的全限定名)else if (resource == null && url == null && mapperClass != null) {// 将Mapper Class的权限定名转化成Class对象Class<?> mapperInterface = Resources.classForName(mapperClass);// 注册进configuration对象的mapperRegistry容器中configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}
}

节点的解析过程

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();

创建XMLMapperBuilder,首先会初始化父类BaseBuilder,并将configuration赋给BaseBuilder;
然后创建MapperBuilderAssistant对象,该对象为XMLMapperBuilder的协助者,用来协助XMLMapperBuilder完成一些解析映射文件的动作。

private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {// 将configuration赋给BaseBuildersuper(configuration);// 创建MapperBuilderAssistant对象(该对象为MapperBuilder的协助者)this.builderAssistant = new  MapperBuilderAssistant(configuration, resource);this.parser = parser;this.sqlFragments = sqlFragments;this.resource = resource;
}//判断是否解析过
public void parse() {// 若当前的Mapper.xml尚未被解析,则开始解析// PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了if (!configuration.isResourceLoaded(resource)) {// 解析<mapper>节点configurationElement(parser.evalNode("/mapper"));// 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析configuration.addLoadedResource(resource);// 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();
}//解析配置文件
private void configurationElement(XNode context) {
try {// 获取<mapper>节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}// 将namespace属性值赋给builderAssistantbuilderAssistant.setCurrentNamespace(namespace);// 解析<cache-ref>节点cacheRefElement(context.evalNode("cache-ref"));// 解析<cache>节点cacheElement(context.evalNode("cache"));// 解析<parameterMap>节点parameterMapElement(context.evalNodes("/mapper/parameterMap"));// 解析<resultMap>节点resultMapElements(context.evalNodes("/mapper/resultMap"));// 解析<sql>节点sqlElement(context.evalNodes("/mapper/sql"));// 解析sql语句      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}

resultMapElements函数解析resultMap节点

 <resultMap id="userResultMap" type="User"><constructor><idArg column="id" javaType="int"/><arg column="username" javaType="String"/></constructor><result property="username" column="user_name"/><result property="password" column="hashed_password"/>
</resultMap>
//解析resultMap节点
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());// 获取<ResultMap>上的id属性String id = resultMapNode.getStringAttribute("id",resultMapNode.getValueBasedIdentifier());// 获取<ResultMap>上的type属性(即resultMap的返回值类型)String type = resultMapNode.getStringAttribute("type",resultMapNode.getStringAttribute("ofType",resultMapNode.getStringAttribute("resultType",resultMapNode.getStringAttribute("javaType"))));// 获取extends属性String extend = resultMapNode.getStringAttribute("extends");// 获取autoMapping属性Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");// 将resultMap的返回值类型转换成Class对象Class<?> typeClass = resolveClass(type);Discriminator discriminator = null;// resultMappings用于存储<resultMap>下所有的子节点List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();resultMappings.addAll(additionalResultMappings);// 获取并遍历<resultMap>下所有的子节点List<XNode> resultChildren = resultMapNode.getChildren();for (XNode resultChild : resultChildren) {// 若当前节点为<constructor>,则将它的子节点们添加到resultMappings中去if ("constructor".equals(resultChild.getName())) {processConstructorElement(resultChild, typeClass, resultMappings);}// 若当前节点为<discriminator>,则进行条件判断,并将命中的子节点添加到resultMappings中去else if ("discriminator".equals(resultChild.getName())) {discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);}// 若当前节点为<result>、<association>、<collection>,则将其添加到resultMappings中去else {// PS:flags仅用于区分当前节点是否是<id>或<idArg>,因为这两个节点的属性名为name,而其他节点的属性名为propertyList<ResultFlag> flags = new ArrayList<ResultFlag>();if ("id".equals(resultChild.getName())) {flags.add(ResultFlag.ID);}resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));}}ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);try {return resultMapResolver.resolve();} catch (IncompleteElementException  e) {configuration.addIncompleteResultMap(resultMapResolver);throw e;}
}

ResultMapResolver的作用是生成ResultMap对象,并将其加入到Configuration对象的resultMaps容器中(具体过程见下)
ResultMapResolver

public ResultMap resolve() {return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);}public ResultMap addResultMap(String id,Class<?> type,String extend,Discriminator discriminator,List<ResultMapping> resultMappings,Boolean autoMapping) {id = applyCurrentNamespace(id, false);extend = applyCurrentNamespace(extend, true);//继承的父ResultMapif (extend != null) {if (!configuration.hasResultMap(extend)) {throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");}ResultMap resultMap = configuration.getResultMap(extend);List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());extendedResultMappings.removeAll(resultMappings);// Remove parent constructor if this resultMap declares a constructor.boolean declaresConstructor = false;for (ResultMapping resultMapping : resultMappings) {if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {declaresConstructor = true;break;}}if (declaresConstructor) {Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();while (extendedResultMappingsIter.hasNext()) {if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {extendedResultMappingsIter.remove();}}}resultMappings.addAll(extendedResultMappings);}ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping).discriminator(discriminator).build();configuration.addResultMap(resultMap);return resultMap;}

sqlElement函数

XMLMapperBuilder解析映射文件中所有的节点,并将这些节点存储在当前映射文件所对应的XMLMapperBuilder对象的sqlFragments容器中,已sql的id为key,XNode为key,供解析sql语句时使用

private void sqlElement(List<XNode> list) throws Exception {if (configuration.getDatabaseId() != null) {sqlElement(list, configuration.getDatabaseId());}sqlElement(list, null);}private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {for (XNode context : list) {String databaseId = context.getStringAttribute("databaseId");String id = context.getStringAttribute("id");id = builderAssistant.applyCurrentNamespace(id, false);if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {sqlFragments.put(id, context);}}}

buildStatementFromContext函数

XMLMapperBuilder将映射文件中的sql语句解析成MappedStatement对象,并存在configuration的mappedStatements容器中。

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

XMLStatementBuilder解析xml的insert等节点

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

MapperBuilderAssistant根据insert节点解析后的元素构建MappedStatement 对象

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

创建SqlSessionFactory对象

看一下SqlSessionFactory的build函数,刚才说了半天,介绍了XMLConfigBuilder解析映射文件的过程,解析完成之后parser.parse()函数会返回一个包含了映射文件解析结果的configuration对象,紧接着,这个对象将作为参数传递给另一个build函数

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

mybatis3源码1-初始化相关推荐

  1. Mybatis3 源码解析系列

    简介 Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis 总结 在MyBatis的学习中,首先通读了<MyBatis3源 ...

  2. MyBatis3源码解析(3)查询语句执行

    简介 上篇探索了MyBatis中如何获取数据库连接,本篇继续探索,来看看MyBatis中如何执行一条查询语句 测试代码 本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备 完整的 ...

  3. MyBatis3源码解析(2)数据库连接

    简介 基于上篇的示例感受,下面我们探索下MyBatis连接数据库的细节是如果实现的,在日常使用中是如何能和Druid数据库连接池等配合起来的 源码解析 基于上篇的示例代码: public class ...

  4. Mybatis3源码分析(05)-加载Configuration-加载MappedStatement

    2019独角兽企业重金招聘Python工程师标准>>> Mybatis3源码分析(05)-加载Configuration-加载MappedStatement 博客分类: java m ...

  5. MyBatis3源码解析(6)TypeHandler使用

    简介 在上几篇中,介绍了MyBatis3对参数和结果的解析转换,对于常规数据类型,默认的处理已经足够应付了,但日常开发中会有一些特殊的类型,就可以通过TypeHandler来进行处理 示例准备 本篇文 ...

  6. MyBatis3源码解析(5)查询结果处理

    简介 上篇中解析了MyBatis3中参数是如何传递处理的,本篇接着看看在获取到查询结果后,MyBatis3是如何将SQL查询结果与我们接口函数定义的返回结果对应的 源码 获取结果后处理的入口 在:My ...

  7. MyBatis3源码解析(4)参数解析

    简介 上篇文章中探索了查询语句的执行过程,下面我们接着来看看其中的查询参数的解析细节,是如何工作的 参数的解析 在日常的开发中,常见的参数有如下几种: 1.直接传入: func(Object para ...

  8. junit源码解析--初始化阶段

    OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...

  9. mybatis 源码分析, 初始化

    分析版本 我们先分析xml配置文件形式的 mybatis, 等我们分析完毕了, 可以去分析 mybatis 和 spring 的对接 pom.xml <dependency><gro ...

  10. MyBatis3源码解析(8)MyBatis与Spring的结合

    简介 在上几篇文章中,解析了MyBatis的核心原理部分,我们大致对其有了一定的了解,接下来我们看看在日常的开发中MyBatis是如何与Spring框架结合的 源码解析 在我们的日常开发中,使用Spr ...

最新文章

  1. UITableView HeaderView,FooterView 使用SnapKit布局导致约束异常
  2. C#线程学习的一点体会和总结
  3. Objetive-C枚举位移操作Swift枚举位移操作
  4. 深入php内核,从底层c语言剖析php实现原理
  5. 总结DevExpress10个使用技巧
  6. Python使用装饰器捕获异常
  7. Nginx与Tomcat、Client之间请求的长连接配置不一致问题解决[转]
  8. ubuntu16.04 kinetic 版本ROS安装PR2
  9. 数学公式编辑器AxMath
  10. 研发管理学习笔记1-研发管理的目标是什么
  11. 在c语言中是闰年的条件为,C语言如何判断是闰年,闰年判断条件?
  12. 机械设备网站建设多少钱
  13. Acrobat如何将word文件转换成pdf格式
  14. Docker搭建 Nginx+PHP+MySQL 环境并部署WordPress实践
  15. 淘宝API,api各种接口
  16. PostMessage,SendMessage,GetMessage,PeekMessage,TranslateMessage,DispatchMessage的用法集合
  17. 毫米波雷达上险量增长超40%:头部厂商放量,伪玩家裸泳
  18. 计算机用户使用品牌调查报告,品牌电脑市场调查报告
  19. 常用排序算法二 希尔-鸡尾酒-堆-桶-基数排序(C++)
  20. 线性代数几何意义与深入理解

热门文章

  1. 【零基础学Java】—继承的概述(十九)
  2. CCF CSP201909-2 小明种苹果(续)
  3. RFID 是什么意思
  4. 退休的姐妹们,你们还打工吗?
  5. 这种一毛钱值多少钱?
  6. 中式装修怎么做,有哪些注意事项?
  7. 盼望的意思是什么,怎么用盼望造句?
  8. 这年头,千万不要花钱创业
  9. 如何把大咖的知识,内容、思维,瞬间变成你的知识内容思维的秘诀
  10. Go语言并发模型:使用 context