分为几个大模块
1.配置文件解析
2.会话创建
3.方法执行
一.配置文件解析
mybatis-config.xml
<configuration>
<properties/>
<settting/>
<typeHandlers/>
<..../>
<mappers/>
</configuration>
mybatis-mapper.xml
<mapper >
<cache/>
<resultMap/>
<select/>
<update/>
<delete/>
<insert/>
</mapper>
mybatis最终会将这两个文件里的元素属性加载到configuration这个类里面
>org.apache.ibatis.session.SqlSessionFactoryBuilder#build()
//1.Config.xml 文件解析
>org.apache.ibatis.builder.xml.XMLConfigBuilder#parse()
>org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration()
>org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement()
// 2.Mapper.xml 文件解析
>org.apache.ibatis.builder.xml.XMLMapperBuilder#parse()
>org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement()
>org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext()
//3.Statemen sql块解析
>org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
>org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement()
// 4.动态SQL脚本解析
>org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource()
>org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode()
>org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags()
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);
//parser.parse()开始解析config.xml文件
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 Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//解析config.xml 的configuration标签,
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//这里就是config.xml里面的所有的标签解析,将所有的属性解析加载到configuration
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(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);
}
}
这里着重说明下mappers这个标签
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//判断mappers下的标签类型,如果是package,代表是包扫描,则走包扫描方法
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//如果不是包扫描,则走直接加载mapper.xml的方法,这里根据标签属性来判断当前mapper资源在哪,
//优先级是,resource>url>class
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());
//加载mapper.xml资源
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.");
}
}
}
}
}
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//加载mapper.xml文件下的mapper标签
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
//获取它的命名空间
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置命名空间
builderAssistant.setCurrentNamespace(namespace);
//缓存引用,这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内 的语句。也许将来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存
cacheRefElement(context.evalNode("cache-ref"));
//是否需要开启二级缓存
cacheElement(context.evalNode("cache"));
//加载参数映射
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//加载结果映射
resultMapElements(context.evalNodes("/mapper/resultMap"));
//加载公用sql
sqlElement(context.evalNodes("/mapper/sql"));
//加载select|insert|update|delete语句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
这里说明我们的sql语句是如何被加载的,buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));
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);
}
}
}
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
//判断当前的id在已经加载了的sql语句中是否存在(这里会在里面做转换,将id转换为当前的命名空间+"."+id)
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
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());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
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;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//将当前的statement添加到mappedStatements这个map中,key为namespace+"."+id
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
会话创建
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(boolean)
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
>org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory#newTransaction()
>org.apache.ibatis.session.Configuration#newExecutor()
>org.apache.ibatis.executor.SimpleExecutor#SimpleExecutor
>org.apache.ibatis.executor.CachingExecutor#CachingExecutor
//执行器插件包装
>org.apache.ibatis.plugin.InterceptorChain#pluginAll(executor)
>org.apache.ibatis.session.defaults.DefaultSqlSession#DefaultSqlSession()
方法的执行
>org.apache.ibatis.session.defaults.DefaultSqlSession#selectList()
>org.apache.ibatis.executor.CachingExecutor#query()
>org.apache.ibatis.executor.BaseExecutor#query()
>org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
>org.apache.ibatis.executor.SimpleExecutor#doQuery
>org.apache.ibatis.session.Configuration#newStatementHandler
>org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandler
>org.apache.ibatis.executor.statement.PreparedStatementHandler#PreparedStatementHandler
>org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler
>org.apache.ibatis.session.Configuration#newParameterHandler
>org.apache.ibatis.plugin.InterceptorChain#pluginAll(parameterHandler)
>org.apache.ibatis.session.Configuration#newResultSetHandler
>org.apache.ibatis.plugin.InterceptorChain#pluginAll(resultSetHandler)
>org.apache.ibatis.plugin.InterceptorChain#pluginAll(statementHandler)
>org.apache.ibatis.executor.SimpleExecutor#prepareStatement
>org.apache.ibatis.executor.BaseExecutor#getConnection
>org.apache.ibatis.executor.statement.BaseStatementHandler#prepare
>org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement
>org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize
>org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
>org.apache.ibatis.type.BaseTypeHandler#setParameter
>org.apache.ibatis.type.UnknownTypeHandler#setNonNullParameter
>org.apache.ibatis.type.IntegerTypeHandler#setNonNullParameter