项目
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 clmstatus
FROM SvcStatement
WHERE 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 #748
String 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赋给BaseBuilder
super(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属性值赋给builderAssistant
builderAssistant.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,而其他节点的属性名为property
List<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);
//继承的父ResultMap
if (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 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))
? 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);
}