mybatis初始化了什么
mybatis初始化工作,主要是初始化configuration配置信息,主要分為兩部分
- 從mybatis-config.xml中讀取的配置
- 從mapper配置文件或Mapper注解讀取的配置
代碼分析
mybatis初始化要經(jīng)過簡單的以下幾步:
調(diào)用SqlSessionFactoryBuilder對象的build(inputStream)方法腹尖;
SqlSessionFactoryBuilder會根據(jù)輸入流inputStream等信息創(chuàng)建XMLConfigBuilder對象;
XMLConfigBuilder對象初始化Configuration對象叫倍;
XMLConfigBuilder調(diào)用parse方法,讀取配置文件信息,更改Configuration對象屬性信息良瞧,并返回Configuration對象
SqlSessionFactoryBuilder根據(jù)Configuration對象創(chuàng)建一個DefaultSessionFactory對象;
SqlSessionFactoryBuilder返回 DefaultSessionFactory對象給Client,供Client使用蹋偏。
-
讀取配置文件流程圖
configuration初始化流程.png
XMLConfigBuilder解析配置文件,并返回Configuration源碼解析
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// xmlConfigBuilder讀取配置文件信息至壤,初始話Configuration信息
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//xmlConfigBuilder調(diào)用parse解析配置文件信息威始,返回Configuration
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.
}
}
}
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
//通過XPathParser解析xml
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//new出一個Configuration對象,對象中有初始化的屬性信息
super(new 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;
//解析configuration節(jié)點下的配置信息
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//讀取properties屬性信息
propertiesElement(root.evalNode("properties"));
//讀取別名信息
typeAliasesElement(root.evalNode("typeAliases"));
//讀取插件信息
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
//讀取全局配置屬性信息
settingsElement(root.evalNode("settings"));
// 多環(huán)境讀取配置
environmentsElement(root.evalNode("environments"));
//多數(shù)據(jù)源配置讀取
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//讀取類型轉(zhuǎn)換器配置信息
typeHandlerElement(root.evalNode("typeHandlers"));
//讀取mappers信息像街,包含resultMpper,sql語句信息
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
propertiesElement源碼解析
private void propertiesElement(XNode context) throws Exception {
//對properties節(jié)點下配置信息進行解析
if (context != null) {
/*讀取properties節(jié)點下所有的<property name="" value=""/>子節(jié)點
*并以name為key,value為key的值注入到一個properties中黎棠,并返回
*/
Properties defaults = context.getChildrenAsProperties();
//獲取properties節(jié)點下的節(jié)點resource配置信息路徑
String resource = context.getStringAttribute("resource");
//獲取properties節(jié)點下的節(jié)點url配置信息路徑
String url = context.getStringAttribute("url");
//url和resource只能配置一個,否則拋出錯誤
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路徑的配置信息镰绎,并返回脓斩,如果普通<property name=""
*value=""/> 有重復key配置,則以resource配置為準
*/
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
/*讀取url路徑的配置信息畴栖,并返回随静,如果普通<property name=""
*value=""/> 有重復key配置,則以url 配置為準
*/
defaults.putAll(Resources.getUrlAsProperties(url));
}
//此為初始化外部傳入的properties配置信息
Properties vars = configuration.getVariables();
if (vars != null) {
//配置文件配置信息 與 外部傳入配置信息合并驶臊,如果有重復key配置挪挤,則以外部為準
defaults.putAll(vars);
}
parser.setVariables(defaults);
//更新configuration的屬性Variables的值
configuration.setVariables(defaults);
}
}
typeAliasesElement源碼解析
private void typeAliasesElement(XNode parent) {
//解析節(jié)點typeAliases節(jié)點配置信息
/*
<typeAliases>
<typeAlias type="com.study.entity.User" alias="User"/>
<package name="com.study.entity"/>
</typeAliases>
*/
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//解析掃描包,進行別名配置
String typeAliasPackage = child.getStringAttribute("name");
// 掃描包下面的類关翎,進行別名配置
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對象和configuration.getTypeAliasRegistry()是同一個
typeAliasRegistry.registerAlias(clazz);
} else {
//注冊別名 typeAliasRegistry對象和configuration.getTypeAliasRegistry()是同一個
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
public void registerAliases(String packageName){
//根據(jù)包名扛门,掃描包下面的類 ,進行注冊別名配置
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//獲取包下面所有的類
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for(Class<?> type : typeSet){
//排除匿名類纵寝、接口類论寨、成員類
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
//獲取類的簡單名稱
String alias = type.getSimpleName();
//查看是否有別名注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
//有別名注解,則別名為注解的值
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
//別名不可為空
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
//把別名小寫
String key = alias.toLowerCase(Locale.ENGLISH);
//configuration中記錄的別名如果存在當前別名爽茴,則拋出錯誤
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);
}
pluginElement源碼解析
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//獲取節(jié)點plugins下面的配置信息
for (XNode child : parent.getChildren()) {
//獲取插件類名稱
String interceptor = child.getStringAttribute("interceptor");
//獲取插件屬性配置
Properties properties = child.getChildrenAsProperties();
//插件都是實現(xiàn) org.apache.ibatis.plugin.Interceptor接口的類葬凳,把插件實例化
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//設置屬性
interceptorInstance.setProperties(properties);
//在configuration插件屬性中,增加插件
configuration.addInterceptor(interceptorInstance);
}
}
}
settingsElement源碼解析
- settings配置信息說明
- 官方配置說明: http://mybatis.org/mybatis-3/zh/configuration.html
image.png
private void settingsElement(XNode context) throws Exception {
if (context != null) {
//獲取所有setting配置參數(shù)信息<setting name="logImpl" value="LOG4J"/>
Properties props = context.getChildrenAsProperties();
// 確定所有的配置信息室奏,是configuration可以識別的
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
/*指定 MyBatis 應如何自動映射列到字段或?qū)傩浴? NONE 表示取消自動映射火焰;
PARTIAL 只會自動映射沒有定義嵌套結果集映射的結果集。
FULL 會自動映射任意復雜的結果集(無論是否嵌套)
*/
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
//全局地開啟或關閉配置文件中的所有映射器已經(jīng)配置的任何緩存胧沫。 configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
//指定 Mybatis 創(chuàng)建具有延遲加載能力的對象所用到的代理工具昌简。
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
/*延遲加載的全局開關占业。當開啟時,所有關聯(lián)對象都會延遲加載纯赎。
特定關聯(lián)關系中可通過設置fetchType屬性來覆蓋該項的開關狀態(tài)谦疾。
*/
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
//當開啟時,任何方法的調(diào)用都會加載該對象的所有屬性犬金。否則念恍,每個屬性會按需加載 configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
//是否允許單一語句返回多結果集(需要兼容驅(qū)動) configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
/*使用列標簽代替列名。不同的驅(qū)動在這方面會有不同的表現(xiàn)晚顷,
具體可參考相關驅(qū)動文檔或通過測試這兩種不同的模式來觀察所用驅(qū)動的結果峰伙。
*/
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
/*
允許 JDBC 支持自動生成主鍵,需要驅(qū)動兼容该默。
如果設置為 true 則這個設置強制使用自動生成主鍵词爬,盡管一些驅(qū)動不能兼容但仍可正常工作(比如 Derby)
*/ configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
/*
配置默認的執(zhí)行器。
SIMPLE 就是普通的執(zhí)行器权均;
REUSE 執(zhí)行器會重用預處理語句(prepared statements)顿膨;
BATCH 執(zhí)行器將重用語句并執(zhí)行批量更新
*/ configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
//設置超時時間,它決定驅(qū)動等待數(shù)據(jù)庫響應的秒數(shù)叽赊。 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
//為驅(qū)動的結果集獲取數(shù)量(fetchSize)設置一個提示值恋沃。此參數(shù)只可以在查詢設置中被覆蓋
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
/*
是否開啟自動駝峰命名規(guī)則(camel case)映射,即從經(jīng)典數(shù)據(jù)庫列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射必指。
*/
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
//允許在嵌套語句中使用分頁(RowBounds)囊咏。如果允許使用則設置為false configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
/*
MyBatis 利用本地緩存機制(Local Cache)防止循環(huán)引用(circular references)和加速重復嵌套查詢。
默認值為 SESSION塔橡,這種情況下會緩存一個會話中執(zhí)行的所有查詢梅割。
若設置值為 STATEMENT,本地會話僅用在語句執(zhí)行上葛家,對相同 SqlSession 的不同調(diào)用將不會共享數(shù)據(jù)
*/ configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
/*
當沒有為參數(shù)提供特定的 JDBC 類型時户辞,為空值指定 JDBC 類型。
某些驅(qū)動需要指定列的 JDBC 類型癞谒,多數(shù)情況直接用一般類型即可底燎,
比如 NULL、VARCHAR 或 OTHER弹砚。
*/ configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
// 指定哪個對象的方法觸發(fā)一次延遲加載 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
//允許在嵌套語句中使用分頁(ResultHandler)双仍。如果允許使用則設置為false configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
//指定動態(tài) SQL 生成的默認語言。 configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
/*
指定當結果集中值為 null 的時候是否調(diào)用映射對象的 setter(map 對象時為 put)方法桌吃,
這對于有 Map.keySet() 依賴或 null 值初始化的時候是有用的朱沃。
注意基本類型(int、boolean等)是不能設置成 null 的
*/ configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
//指定 MyBatis 增加到日志名稱的前綴。
configuration.setLogPrefix(props.getProperty("logPrefix"));
// 指定 MyBatis 所用日志的具體實現(xiàn)逗物,未指定時將自動查找呕屎。
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
/*
指定一個提供Configuration實例的類。
這個被返回的Configuration實例用來加載被反序列化對象的懶加載屬性值敬察。
這個類必須包含一個簽名方法static Configuration getConfiguration(). (從 3.2.3 版本開始)
*/ configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
}
environmentsElement源碼解析
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//如果外部沒有傳入environment,則用default標簽指定的environment
environment = context.getStringAttribute("default");
}
//獲取所有的環(huán)境配置信息
for (XNode child : context.getChildren()) {
//后去當前節(jié)點尔当,環(huán)境序號ID
String id = child.getStringAttribute("id");
//看當前節(jié)點id序號是否和上面environment一致莲祸,如果environment為空,則拋出錯誤
if (isSpecifiedEnvironment(id)) {
//獲取事物管理配置信息 可以用別名椭迎,也可以指定類
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//獲取連接池配置信息
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//根據(jù)配置信息锐帜,new出環(huán)境environment的builder類,注入環(huán)境序號畜号、事物管理缴阎、連接池信息
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//調(diào)用build()方法,返回Environment類简软,注入到configuration環(huán)境屬性中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
databaseIdProviderElement源碼解析
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
/*配置案例
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
*/
if (context != null) {
String type = context.getStringAttribute("type");
// DB_VENDOR為別名蛮拔,對應類
//typeAliasRegistry.registerAlias("DB_VENDOR",VendorDatabaseIdProvider.class);
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) {
/*獲取databaseId,當databaseId不為空時,解析sql語句時痹升,會過濾sql語句中databaseId不匹配的配置信息
案例 此sql配置的databaseId是oracle,當前environment的數(shù)據(jù)庫是mysql,則在讀取配置信息時建炫,會把這個sql配置信息過濾掉
<insert id="insert" parameterType="com.study.entity.User" databaseId="oracle">
insert into study_user (id,name,password,age,deleteFlag) values (#{id},#{name},#{password},#{age},#{deleteFlag})
</insert>
*/
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
//設置configuration的databaseId屬性信息
configuration.setDatabaseId(databaseId);
}
}
typeHandlerElement源碼解析
private void typeHandlerElement(XNode parent) throws Exception {
//分析節(jié)點typeHandlers下的類型轉(zhuǎn)換器配置信息
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//通過掃描包,分析類型轉(zhuǎn)換器配置信息
String typeHandlerPackage = child.getStringAttribute("name");
//類型轉(zhuǎn)換器通過包名注冊 類型轉(zhuǎn)換器信息
typeHandlerRegistry.register(typeHandlerPackage);
} else {
/*
<typeHandlers>
<typeHandler handler="" javaType="" jdbcType=""/>
<package name=""/>
</typeHandlers>
*/
//獲取節(jié)點typeHandler 的javaType
String javaTypeName = child.getStringAttribute("javaType");
//獲取節(jié)點typeHandler 的jdbcType
String jdbcTypeName = child.getStringAttribute("jdbcType");
//獲取節(jié)點typeHandler 的handler類
String handlerTypeName = child.getStringAttribute("handler");
//反射獲取javaType的class
Class<?> javaTypeClass = resolveClass(javaTypeName);
//通過JdbcType.valueOf(alias)疼蛾,獲取枚舉JdbcType
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
//通過反射獲取 類轉(zhuǎn)換的Class 接口TypeHandler的實現(xiàn)實例
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
//注冊類轉(zhuǎn)換信息
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
//注冊類轉(zhuǎn)換信息
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
//注冊類轉(zhuǎn)換信息
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
- TypeHandlerRegistry的屬性信息
//jdbctype對應的typeHandler
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
//Type是所有類的始祖肛跌,javaTYpe對應所有的jdbcType的typeHandler
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
//未知的類轉(zhuǎn)換器
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
//class對應的類轉(zhuǎn)換器
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//查看是否有 MappedJdbcTypes注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
//如果有注解則調(diào)用次方法,注冊類轉(zhuǎn)換器配置信息
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
//查看是否已經(jīng)有javaType的類轉(zhuǎn)換配置信息
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
//沒有則new出一個hashMap
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}
//所有java類 類轉(zhuǎn)換信息 加入配置信息
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
mapperElement源碼解析
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//解析節(jié)點mappers 下的配置數(shù)據(jù)
for (XNode child : parent.getChildren()) {
//解析mppers下節(jié)點package,通過包掃描接口
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);
//獲取mapper.xml的inputStream流
InputStream inputStream = Resources.getResourceAsStream(resource);
//通過XMLMapperBuilder解析mapper.xml配置信息 SqlFragments為靜態(tài)sql語句
/*
<sql id="studentProperties"><!--sql片段-->
select
stud_id as studId
, name, email
, dob
, phone
from students
</sql>
可以通過<include refid="studentProperties"></include><!--復用-->c
*/
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.");
}
}
}
}
}
- 掃描包mapper解析源碼分析
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
- mapperRegistry類屬性
//全局configuration
private final Configuration config;
//接口 對應的代理類工廠 和spring集成需要用到這個
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> void addMapper(Class<T> type) {
//解析接口類
if (type.isInterface()) {
//接口類如果已經(jīng)解析過察郁,則拋出錯誤
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//在knownMappers中放入接口類衍慎,接口類代理工廠的信息
knownMappers.put(type, new MapperProxyFactory<T>(type));
//new 出MapperAnnotationBuilder注解解析器
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
- 通過 MapperAnnotationBuilder進行解析
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
//sqlAnnotationTypes注解
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
//sqlProviderAnnotationTypes注解 兩種注解不可以同時存在
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
//查看是否已經(jīng)加載過
if (!configuration.isResourceLoaded(resource)) {
//查找下面的xml文件,接口同名以'.xml'結尾
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
//根據(jù)方法注解皮钠,來解析mapper接口
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
//過濾掉橋接方法
if (!method.isBridge()) {
//解析方法稳捆,如果當前命名空間+method名稱已經(jīng)解析過,則拋出錯誤
/*Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
重寫了put方法麦轰,會判斷是否key已經(jīng)存在眷柔,存在會拋出錯誤
*/
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
- XMLMapperBuilder解析xml過程解析
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//解析xml文件下mapper節(jié)點下的配置信息
configurationElement(parser.evalNode("/mapper"));
//記錄該資源已經(jīng)解析過
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
//獲取命名空間名稱
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//解析助理 設置當前命名空間名稱
builderAssistant.setCurrentNamespace(namespace);
//緩存配置解析
cacheRefElement(context.evalNode("cache-ref"));
//緩存配置解析
cacheElement(context.evalNode("cache"));
//不建議使用
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);
}
}
?? Mybatis初始化過程中,解析parameterMap原朝、resultMap驯嘱、"select|insert|update|delete"元素,無疑是重頭戲
??元素parameterMap將會解析為ParameterMap對象喳坠,該對象包含一個List<ParameterMapping>集合鞠评,是one-to-many關系。
?? 元素resultMap將會解析為ResultMap對象壕鹉,該對象包含一個List<ResultMapping>集合剃幌,是one-to-many關系聋涨。
??元素"select|insert|update|delete"將會被解析為MappedStatement對象,該對象包含了ParameterMap负乡、ResultMap等對象
- resultMap解析
- ResultMap 類屬性
//resultMap的id值
private String id;
//對應的java類
private Class<?> type;
//所有的resultMapping對象牍白,包括constructor/idArg,constructor/arg,result,association,collection,
//但不包括association和collection里的子節(jié)點
private List<ResultMapping> resultMappings;
//包括constructor/idArg,id
private List<ResultMapping> idResultMappings;
//constructor里的子節(jié)點
private List<ResultMapping> constructorResultMappings;
//除constructor里的子節(jié)點,其他都是,result,association,collection,id
private List<ResultMapping> propertyResultMappings;
//所有被映射的列
private Set<String> mappedColumns;
//通過case來判斷映射類抖棘,比較少用
private Discriminator discriminator;
//是否有內(nèi)映射茂腥,association, collection都為內(nèi)映射,
//內(nèi)查詢不算(就是的reulst節(jié)點中配置select屬性的情況)
private boolean hasNestedResultMaps;
//是否有查詢,
private boolean hasNestedQueries;
//是否要求自動映射
private Boolean autoMapping;
- ResultMap.Builder.build()方法
public ResultMap build() {
//id不可為空
if (resultMap.id == null) {
throw new IllegalArgumentException("ResultMaps must have an id");
}
//映射的列
resultMap.mappedColumns = new HashSet<String>();
//包括constructor/idArg,id
resultMap.idResultMappings = new ArrayList<ResultMapping>();
//constructor里的子節(jié)點
resultMap.constructorResultMappings = new ArrayList<ResultMapping>();
//除constructor里的子節(jié)點,其他都是
resultMap.propertyResultMappings = new ArrayList<ResultMapping>();
for (ResultMapping resultMapping : resultMap.resultMappings) {
//如果其中一個resultMapping有內(nèi)查詢切省,則這個resultMap也就是有內(nèi)查詢
resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
//如果其中一個resultMapping有內(nèi)映射最岗,則這個resultMap也就是有內(nèi)映射
resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
//獲取字段名稱
final String column = resultMapping.getColumn();
if (column != null) {
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
} else if (resultMapping.isCompositeResult()) {
//組合配置分析
for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
final String compositeColumn = compositeResultMapping.getColumn();
if (compositeColumn != null) {
resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
}
}
}
//如果是構造器resultMapping,則放入constructorResultMappings
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
resultMap.constructorResultMappings.add(resultMapping);
} else {
//其余的放入propertyResultMappings
resultMap.propertyResultMappings.add(resultMapping);
}
//ID的類型resultMapping放入idResultMappings
if (resultMapping.getFlags().contains(ResultFlag.ID)) {
resultMap.idResultMappings.add(resultMapping);
}
}
//如果沒有指定ID,idResultMappings包含所有的resultMapping
if (resultMap.idResultMappings.isEmpty()) {
resultMap.idResultMappings.addAll(resultMap.resultMappings);
}
//集合為只讀的
resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
return resultMap;
}
- 增刪改查sql語句解析
- 一個MappedStatement對象對應Mapper配置文件中的一個select/update/insert/delete節(jié)點朝捆,主要描述的是一條SQL語句
/*
* MappedStatement所有的屬性方法
*/
private String resource;
private Configuration configuration;
//節(jié)點中的id屬性加要命名空間
private String id;
private Integer fetchSize;
//超時時間
private Integer timeout;
//statement類型 STATEMENT, PREPARED, CALLABLE
private StatementType statementType;
//結果集類型
private ResultSetType resultSetType;
//對應一條SQL語句 動態(tài)sql語句封裝在這里
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
//是否啟用緩存
private boolean useCache;
private boolean resultOrdered;
//SQL的類型,select/update/insert/detete
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
//是否有內(nèi)映射
private boolean hasNestedResultMaps;
//數(shù)據(jù)庫ID
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
public void parseStatementNode() {
//獲取id屬性
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());
// 解析selectKey
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// 解析sql語句
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();
}
- SqlSource 解析
- SqlSource接口
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
public class BoundSql {
//解析后的最終sql語句般渡,可能帶有問號 "?"
private String sql;
//參數(shù)
private List<ParameterMapping>
parameterMappings;
private Object parameterObject;
private Map<String, Object> additionalParameters;
private MetaObject metaParameters;
}
public SqlSource parseScriptNode() {
//解析sql語句,把節(jié)點下的sql語句信息芙盘,封裝到SqlNode中
List<SqlNode> contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
//是否是動態(tài)
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
//遞歸調(diào)用此方法
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);
//是否是動態(tài) ${}為動態(tài)
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
}
//含有set if where trim等節(jié)點
else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNode().getNodeName();
//獲取對應的nodeHandler驯用,所有類型的nodeHandler看下面的代碼
NodeHandler handler = nodeHandlers(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
//解析此節(jié)點,會遞歸調(diào)用當前方法
handler.handleNode(child, contents);
isDynamic = true;
}
}
return contents;
}
//所有nodeHandler類型
NodeHandler nodeHandlers(String nodeName) {
Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
map.put("trim", new TrimHandler());
map.put("where", new WhereHandler());
map.put("set", new SetHandler());
map.put("foreach", new ForEachHandler());
map.put("if", new IfHandler());
map.put("choose", new ChooseHandler());
map.put("when", new IfHandler());
map.put("otherwise", new OtherwiseHandler());
map.put("bind", new BindHandler());
return map.get(nodeName);
}