- 前面介紹了myabtis的基礎組件部分,感覺起來非常的零散,沒有系統(tǒng)性。但是前面部分的內(nèi)容是基礎,了解了前面基礎組件部分的內(nèi)容,在跟著流程去看源碼,就不會云里霧里了,如果不看前面的基礎,蒙著頭往mybatis源碼里面去撞,基本會迷路摸不清方向。下面我們將跟著myabtis的執(zhí)行流程去分析源碼,首先要分析的是mybatis的初始化過程。
- 在將mybatis初始化過程之前,先來看看mybatis的的簡單執(zhí)行代碼。
//開始初始化
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
//獲取會話并開始執(zhí)行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("com.huya.qiu.mapper.UserMapper.getAllUser");
一、mybatis初始化過程
- *在mybatis初始化過程中主要完成的是config配置、mapper配置和相關注解信息的解析,初始化入口就是SqlSessionFactoryBuilder.buidler()方法。
public SqlSessionFactory build(Reader reader, String environment,
Properties properties) {
try {
1、讀取配置
XMLConfigBuilder parser = new XMLConfigBuilder(reader,
environment, properties);
2、解析配置文件得到Configuration對象,創(chuàng)建DefaultSqlSessionFactory對象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
從上面可以看出,build方法主要做的就是兩件事(1)創(chuàng)建XMLConfigBuilder 對象(2)解析配置文件得到Configuration對象,創(chuàng)建DefaultSqlSessionFactory對象。下面針對這兩個步驟進行詳細的分析。
1、XMLConfigBuilder(解析核心配置文件)
-
扮演具體建造者的角色,主要負責解析config配置文件的解析并繼承BaseBuilder類。
1、BaseBuilder
是mybatis中有關建造者的抽象類,扮演著建造者模式中的建造者接口角色,具體的類結構如圖,BaseBuilder擁有眾多的實現(xiàn)類,這些實現(xiàn)類都扮演著具體場合的建造者角色:
BaseBuilder類結構圖
public abstract class BaseBuilder {
//引用configuration對象
protected final Configuration configuration;
//TypeAliasRegistry會記錄別名
protected final TypeAliasRegistry typeAliasRegistry;
//TypeHandlerRegistry加注冊類型轉換器(自定義或者默認)
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
...提供了一系列的resolveTypeHandler、resolveResultSetType等抽象方法
2、XMLConfigBuilder具體實現(xiàn)
public class XMLConfigBuilder extends BaseBuilder {
//標志是否已經(jīng)解析過配置文件
private boolean parsed;
//用于加息配置文件的XpathParse對象
private final XPathParser parser;
//<enviroment>標簽
private String environment;
//負責創(chuàng)建和緩存Reflector對象
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
//構造函數(shù)
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;
}
//解析config配置文件的入口
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//查找<configuration>節(jié)點,開始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//解析<properties>節(jié)點
propertiesElement(root.evalNode("properties"));
//解析<settings>節(jié)點
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//解析<typeAliases>節(jié)點
typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins>節(jié)點
pluginElement(root.evalNode("plugins"));
//解析<objectFactory>節(jié)點
objectFactoryElement(root.evalNode("objectFactory"));
//解析<objectWrapperFactory>節(jié)點
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析<reflectorFactory>節(jié)點
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);//將值設置到setting中
//解析<environments>節(jié)點
environmentsElement(root.evalNode("environments"));
//解析<databaseIdProvider>節(jié)點
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析<typeHandlers>節(jié)點
typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>節(jié)點
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
- parseConfiguration方法是最終執(zhí)行配置文件解析的地方,從方法中可以看出解析的順序是按著核心配置文件中節(jié)點配置順序來的,因此如果我們在配置核心配置文件的時候,是要按著節(jié)點順序來的。下面針對方法中每一個節(jié)點的解析詳細介紹:
(1)解析<properties>節(jié)點 - 解析后形成Properties對象,之后將該字段設置到XpathParser和Configuration的variables字段中。
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//解析相應的name和value屬性記錄到properties中
Properties defaults = context.getChildrenAsProperties();
//解析reource和url屬性用于確定配置文件位置,且這兩個不能同時存在
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("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) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
//更新xpathParse和configuration的variables字段
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
(2)解析<settings>節(jié)點
- settings設置會改變mybatis的運行時行為,在初始化的時候這些全局配置信息都會被記錄到configuration對象的對應屬性中
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
//解析settings子節(jié)點的name和value屬性并返回properties對象
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
//創(chuàng)建metaClass
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
//檢測key指定的屬性在configuration類中是都有對應的setter方法
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
(3)解析解析<typeAliases>節(jié)點
- typeAliases與別名有關,前面知識可知解析后在TypeAliasRegistry中注冊。
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//處理package節(jié)點
if ("package".equals(child.getName())) {
//獲取包名
String typeAliasPackage = child.getStringAttribute("name");
//注冊包下的所有別名,放入hash中
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
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);
}
}
}
}
}
(4)解析<typeHandlers>節(jié)點
- 通過TypeHandlerRegistry對象完成TypeHandler的注冊實現(xiàn)原理和上面的相似
(5)解析<plugins>節(jié)點
- 插件是mybatis提供的擴展機制之一,用于通過添加自定義的插件對sql語句執(zhí)行過程中某一個點進行攔截。自定義插件需要實現(xiàn)Interceptor接口并通過注解指定需要攔截的方法簽名
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//獲取<interceptor>節(jié)點的屬性的值
String interceptor = child.getStringAttribute("interceptor");
//獲取節(jié)點下<properties>配置信息形成properties對象
Properties properties = child.getChildrenAsProperties();
//進行屬性別名注冊,并實例化攔截器對象
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//設置屬性
interceptorInstance.setProperties(properties);
//加載到configuration
configuration.addInterceptor(interceptorInstance);
}
}
}
protected Class<?> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
protected Class<?> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
(6)解析<objectFactory>,<objectWrapperFactory>,<reflectorFactory>等節(jié)點
- 邏輯和解析<plugins>節(jié)點差不多
(7)解析<environments>節(jié)點
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//未指定enviroment字段則就只用default指定
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//與environment字段匹配
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//創(chuàng)建datasourcefactory和datasource
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//建造者模式
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//記錄到environment字段中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private boolean isSpecifiedEnvironment(String id) {
if (environment == null) {
throw new BuilderException("No environment specified.");
} else if (id == null) {
throw new BuilderException("Environment requires an id attribute.");
} else if (environment.equals(id)) {
return true;
}
return false;
}
(8)解析<databaseIdProvider>節(jié)點
- 可以通過databaseIdProvider定義所支持的數(shù)據(jù)庫產(chǎn)品databaseId,然后在映射配置文件中定義sql語句節(jié)點時候,通過databaseid指定該sql語句應用的數(shù)據(jù)庫產(chǎn)品。實現(xiàn)幫助開發(fā)人員屏蔽多種數(shù)據(jù)庫產(chǎn)品在sql語句方面的差異。
- mybatis中根據(jù)datasource確定數(shù)據(jù)庫產(chǎn)品類型,然后在解析映射配置文件時候,加載不帶databaseId屬性和帶有匹配當前數(shù)據(jù)庫databaseId屬性的所有sql語句,如果同時找到name拋棄不帶databaseId的相同語句。
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
Properties properties = context.getChildrenAsProperties();
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
//通過datasource獲取databaseId,記錄到configuration.databaseId字段中
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
(9)解析<mapper>節(jié)點
- 在加載config配置文件時候,<mapper>節(jié)點節(jié)點會告訴mybatis去哪些位置查找映射配置文件以及使用配置注解標識的接口
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//package子節(jié)點
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
//向mapperRegistry注冊mapper接口
configuration.addMappers(mapperPackage);
} else {
//回去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);
//創(chuàng)建XMLMapperBuilder,解析映射配置文件
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.");
}
}
}
}
}
二、XMLMapperBuilder(解析映射配置文件)
- 負責解析映射配置文件,繼承了BaseBuilder抽象類。也是具體的建造者角色,parse方法是其入口
public void parse() {
//判斷是否已經(jīng)加載了該映射文件
if (!configuration.isResourceLoaded(resource)) {
//處理<mapper>節(jié)點
configurationElement(parser.evalNode("/mapper"));
//將resource添加到集合中保存,記錄加載過的映射文件
configuration.addLoadedResource(resource);
//注冊mapper接口
bindMapperForNamespace();
}
//處理configurationElement中解析失敗的<resultMap>節(jié)點
parsePendingResultMaps();
//處理configurationElement中解析失敗的<cache-ref>節(jié)點
parsePendingCacheRefs();
//處理configurationElement中解析失敗的sql語句節(jié)點
parsePendingStatements();
}
//封裝了每一個節(jié)點的解析過程
private void configurationElement(XNode context) {
try {
//1.獲取<mapper>節(jié)點的namespace屬性,并記錄命名空間
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
//2.解析cache-ref節(jié)點
cacheRefElement(context.evalNode("cache-ref"));
//3.解析cache節(jié)點
cacheElement(context.evalNode("cache"));
//已經(jīng)廢棄
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//4.解析resultMap節(jié)點
resultMapElements(context.evalNodes("/mapper/resultMap"));
//5.解析sql節(jié)點
sqlElement(context.evalNodes("/mapper/sql"));
//6.解析select|insert|update|delete等sql節(jié)點
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);
}
}
- 從方法中可以得出配置文件的解析最終也是歸屬到單個節(jié)點的解析
(1)解析cache節(jié)點 - mybatis默認沒有開啟二級緩存,要開啟需要在映射配置文件中添加cache節(jié)點
private void cacheElement(XNode context) throws Exception {
if (context != null) {
//獲取cache節(jié)點的type屬性,默認是PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
//查找type屬性對應的cache接口歐
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//獲取eviction屬性默認是LRU
String eviction = context.getStringAttribute("eviction", "LRU");
//解析eviction屬性指定的cache裝飾器類型
Class<? extends Cache> evictionClass =
typeAliasRegistry.resolveAlias(eviction);
//獲取flushInterval屬性默認是null
Long flushInterval = context.getLongAttribute("flushInterval");
//獲取size屬性值
Integer size = context.getIntAttribute("size");
//獲取readOnly屬性值
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
//通過builderAssistant創(chuàng)建Cache對象,并添加到caches集合中保存
builderAssistant.useNewCache(typeClass, evictionClass,
flushInterval, size, readWrite, blocking, props);
}
}
------------------------------------------------------------------------------
builderAssistant類中的useNewCache方法
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
//利用構建器構建出緩存
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
(2)解析cache-ref
- 如果想實現(xiàn)一個二級緩存在多個namespace之間共用可以配置cache-ref節(jié)點
(3)解析resultMap節(jié)點 - resultMap定義了結果集與javabean之間的映射規(guī)則
ResultMap對象
每一個resultMap節(jié)點會被映射成一個ResultMap對象,而每一個節(jié)點所定義的映射關系使用ResultMapping對象表示
也就是說一個ResultMap對應resultMap節(jié)點,ResultMapping對應resultMap屬性
public class ResultMap {
private Configuration configuration;
private String id;//節(jié)點id
private Class<?> type;//節(jié)點類型
private List<ResultMapping> resultMappings;//其他映射關系集合
private List<ResultMapping> idResultMappings;//記錄映射關系帶有id標識的映射關系,id節(jié)點和constructor節(jié)點的idArg子節(jié)點
private List<ResultMapping> constructorResultMappings;//constructor所有子節(jié)點
private List<ResultMapping> propertyResultMappings;//不帶有constructor標識的映射關系
private Set<String> mappedColumns;//記錄所有涉及column屬性的集合
private Set<String> mappedProperties;//記錄所有propertes屬性的集合
private Discriminator discriminator;//鑒別器節(jié)點
private boolean hasNestedResultMaps;//含有resultMap屬性不含有resultSet屬性則為true
private boolean hasNestedQueries;//存在select屬性,為true
private Boolean autoMapping;//是否開啟自動映射
...
resultMapping對象
- 記錄了結果集中的一列與JavaBean中的一個屬性之間的映射關系,也對應resultMap中的屬性
public class ResultMapping {
private Configuration configuration;
private String property;//對應節(jié)點的property屬性,表示的是該列進行映射的屬性
private String column;//對應節(jié)點的column屬性,表示從數(shù)據(jù)庫中得到的列名或者列名的別名
private Class<?> javaType;//對應節(jié)點的javaType屬性,表示的是一個JavaBean的完全限定名,后者一個類型別名
private JdbcType jdbcType;//對應節(jié)點的jdbcType屬性,表示映射列的JDBC類型
private TypeHandler<?> typeHandler;//對應節(jié)點的typeHandler,表示類型處理器
private String nestedResultMapId;//對應節(jié)點的resultMap屬性,通過id引用另一個resultMap節(jié)點的定義
private String nestedQueryId;//對應節(jié)點的select屬性,通過id應用到另一個select節(jié)點定義
private Set<String> notNullColumns;
private String columnPrefix;
private List<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet;//對應resultSet屬性
private String foreignColumn;
private boolean lazy;//是否延遲加載,對應fetchType屬性
...
resultMapElements方法解析<resultMap>節(jié)點
private void resultMapElements(List<XNode> list) throws Exception {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
//處理每一個resultMap節(jié)點
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//獲取id屬性,默認會拼裝所有父節(jié)點的id或者value后者Property屬性值
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
//獲取type屬性,表示結果集將被映射成type指定類型的對象(也就是如果有ofType、resultType等最后映射成type)
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//后去extends屬性,指定繼承關系
String extend = resultMapNode.getStringAttribute("extends");
//是否是自動映射,及自動查找與列名同名的屬性名
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//加息type類型
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
//處理resultMap子節(jié)點
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
//處理constructor子節(jié)點
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
//處理id、result、association、collection等子節(jié)點
List<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
//創(chuàng)建ResultMapping對象,添加到resultmappings集合中保存
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
//創(chuàng)建ResultMap對象,添加到Configuration.resultmaps集合中去
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
//創(chuàng)建相應的ResultMapping對象
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
String property;
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
property = context.getStringAttribute("property");
}
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resultSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
整個解析過程還是很復雜的,建議去看源碼,這里就不貼代碼了(ps:自己這一部分也是看的半懂,后面有時間回看一遍)
(4)sql節(jié)點解析
- 使用sql節(jié)點可以增加sql語句片段的重復利用,只需要使用include引入就好了
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {//遍歷sql節(jié)點
//獲取databaseiId
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
//為id添加命名空間
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
//記錄到sqlFragments集合中保存
sqlFragments.put(id, context);
}
}
}
(5)sql語句相關節(jié)點解析
待定...
