文章目錄
該節內容主要將Mybatis初始化內容,回顧下我們第一節的核心重點圖。
舉個case:Mybatis初始化的加載過程時序圖
3.1、XML的配置解析示例
3.1.1、mybatis-config.xml的使用示例
//1、xml的協議頭
<?xml version="1.0" encoding="UTF-8" ?>
//2、mybatis對於xml,Configuration配置文件的約束協議
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
//3、全局配置對象
<configuration>
//3.1.1、數據源配置1:通過屬性注入數據源
<properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/>
//3.1.2、數據源配置2:通過直接配置
//3.2、全局配置對象的屬性
<settings>
//是否開啓緩存
<setting name="cacheEnabled" value="true"/>
//是否開啓懶加載
<setting name="lazyLoadingEnabled" value="false"/>
//是否允許返回多個結果集
<setting name="multipleResultSetsEnabled" value="true"/>
//是否使用駝峯標示屬性
<setting name="useColumnLabel" value="true"/>
//是否使用生成主鍵策略
<setting name="useGeneratedKeys" value="false"/>
//默認的執行器類型
<setting name="defaultExecutorType" value="SIMPLE"/>
//默認的會話超時時間單位s
<setting name="defaultStatementTimeout" value="25"/>
</settings>
//3.3、實體對象別名映射
<typeAliases>
<typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author"/>
</typeAliases>
//3.3、指定類型處理器
<typeHandlers>
<typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
</typeHandlers>
//3.4、對象工廠
<objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
<property name="objectFactoryProperty" value="100"/>
</objectFactory>
//3.5、配置會話攔截插件
<plugins>
<plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
<property name="pluginProperty" value="100"/>
</plugin>
</plugins>
//3.6、配置環境變量: MyBatis 系統的核心設置,包括獲取數據庫連接實例的數據源(DataSource)以及決定事務作用域和控制方式的事務管理器(TransactionManager)
<environments default="development">
//3.6.1、環境配置變量可以通過ID來區分dev-uat-pre-pro
<environment id="development">
//3.6.1.1、事務管理器
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
//3.6.1.2、數據源配置
<dataSource type="UNPOOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
//3.7、mapper接口配置,包含了 SQL 代碼和映射定義信息
<mappers>
<mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
</mappers>
</configuration>
3.1.2、Mapper.xml的使用示例
//1、xml的定義
<?xml version="1.0" encoding="UTF-8" ?>
//2、mapper協議的約束
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
//3、映射類路徑的mapper,實際上是和對應的java類綁定
<mapper namespace="org.apache.ibatis.domain.blog.mappers.AuthorMapper">
//4、mapper接口方法:根據主鍵ID查詢返回結果map的
<parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
<parameter property="id" />
</parameterMap>
//5、mapper接口方法:對於實體對象和數據庫字段的映射情況
<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
//字段列和對象屬性的映射
<id column="id" property="id" />
<result property="username" column="username" />
<result property="favouriteSection" column="favourite_section" />
</resultMap>
//6、mapper接口方法:包含嵌套的對象屬性字段映射情況
<resultMap id="selectImmutableAuthor" type="org.apache.ibatis.domain.blog.ImmutableAuthor">
//參數和Java類型映射
<constructor>
<idArg column="id" javaType="_int" />
<arg column="username" javaType="string" />
<arg column="favourite_section" javaType="org.apache.ibatis.domain.blog.Section" />
</constructor>
</resultMap>
//7、mapper接口方法:查詢所有結果
<select id="selectAllAuthors" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//8、mapper接口方法:查詢返回set集
<select id="selectAllAuthorsSet" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//9、mapper接口方法:查詢返回list集合
<select id="selectAllAuthorsLinkedList" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//10、mapper接口方法:查詢返回數組
<select id="selectAllAuthorsArray" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//11、mapper接口方法:查詢只返回部分參數
<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap">
select id, username from author where id = #{value}
</select>
//12、mapper接口方法:查詢綁定外部傳入參數
<select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id = ?
</select>
//13、mapper接口方法:查詢指定入參類型的參數
<select id="selectAuthorWithInlineParams" parameterType="int"
resultType="org.apache.ibatis.domain.blog.Author">
select * from author where id = #{id}
</select>
//14、mapper接口方法:插入實體對象映射
<insert id="insertAuthor" parameterType="org.apache.ibatis.domain.blog.Author">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
//15、mapper接口方法:更新實體對象映射
<update id="updateAuthor" parameterType="org.apache.ibatis.domain.blog.Author">
update Author
set username=#{username,
javaType=String},
password=#{password},
email=#{email},
bio=#{bio}
where id=#{id}
</update>
//16、mapper接口方法:刪除實體根據主鍵ID
<delete id="deleteAuthor" parameterType="int">
delete from Author where id = #{id}
</delete>
//17、mapper接口方法:有選擇更新字段
<update id="updateAuthorIfNecessary" parameterType="org.apache.ibatis.domain.blog.Author">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
//18、mapper接口方法:根據指定的條件進行查詢
<select id="selectWithOptions" resultType="org.apache.ibatis.domain.blog.Author"
fetchSize="200" timeout="10" statementType="PREPARED" resultSetType="SCROLL_SENSITIVE" flushCache="false" useCache="false">
select * from author
</select>
</mapper>
3.1.3、代碼中XML的配置解析使用示例
3.1.3.1、從 XML 中構建 SqlSessionFactory
根據Mybatis核心設計圖其實可以觀察到,整個Mybatis的核心在於一個SqlSession的一系列操作:實體對象和SQL的綁定,執行會話,結果集處理。而SqlSession的管理則來自於SqlSessionFactory,SqlSessionFactory是可以通過SqlSessionFactoryBuilder獲得構建。如何構建出符合用戶的SqlSessionFactory,則依賴於我們上面提到的Configuration 實例(Mybatis-config.xml)。
⚠️:強調下重點:每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲核心的,SqlSessionFactory 的實例是通過SqlSessionFactoryBuilder從Configuration 實例(Mybatis-config.xml)構建出來的。
從 XML 文件中構建 SqlSessionFactory 的實例非常簡單,可以使用類路徑下的資源文件進行配置。也可以使用任意的輸入流(InputStream)實例,比如用文件路徑字符串或 file:// URL 構造的輸入流。MyBatis 包含一個名叫 Resources 的工具類可以通過它來獲取資源輸入流(可以參考之後的源碼明細篇)。
//我們上面3.1.1節提到的配置文件全路徑
String resource = "org/mybatis/example/mybatis-config.xml";
//通過Mybatis提供的Resources工具類獲取輸入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//根據Mybatis提供的SqlSessionFactoryBuilder構建器構建用戶定義的Configuration特有的SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3.1.3.2、構建我們真正幹活的SqlSession
//SqlSessionFactory 中獲取 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession()
//執行上面我們3.1.2節提到的配置文件mapper中的方法,因爲xml配置好的映射關係所以我們直接是通過sqlSession就可以拿到映射的mapper,怎麼拿到的呢?依然是需要看下第二章中我提到的Mybatis核心設計圖,通過動態代理的方式從Configuration對象中找到對應的mapper,method,parameter。所以從這裏看Configuration就是整個一個倉庫擁有所有你需要的東西。
//從會話中獲取綁定好的mapper接口信息
AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class);
//執行mapper接口的實現方法,而這段具體分析就到第四章和第五章可以學習源碼,第三章主要將全局初始化這個過程。
Author author = mapper.selectAuthorInfoById(101);
3.2、註解的配置解析示例
3.2.1、Mybatis-config.xml的使用示例
當然這裏也可以使用properties文件屬性等內容,或者springboot的yml文件等配置。
//1、xml的協議約束
<?xml version="1.0" encoding="UTF-8" ?>
//2、Configuration的協議約束
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
//3、全局配置類的約束
<configuration>
//3.1、全局配置的屬性和事務管理器
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:automapping"/>
<property name="username" value="sa"/>
</dataSource>
</environment>
</environments>
</configuration>
3.2.2、Mapper接口的使用示例
根據我們接口常用的CRUD操作這裏分別提供了註解的實現方法形式。
public interface AuthorMapperWithAnnotation {
//使用select註解直接查詢
@Select("select id, username, password, email, bio, favourite_section from author where id = #{id}")
Author selectAuthorInfoById(int id);
//使用SelectProvider註解直接查詢
@SelectProvider(type =AuthorProvider.class,method = "selectAuthorWithPlaceholder")
Author selectAuthorWithPlaceholder(Author author);
//使用Insert註解直接查詢
@Insert("insert into author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio})")
int insertAuthor(Author author);
//使用InsertProvider註解直接查詢
@InsertProvider(type =AuthorProvider.class,method = "insertAuthorWithPlaceholder")
int insertAuthorWithPlaceholder(Author author);
//使用Update註解直接查詢
@Update("update author set username=#{username} where id =#{id}")
int updateAuthor(Author author);
//使用UpdateProvider註解直接查詢
@UpdateProvider(type =AuthorProvider.class,method = "updateAuthorWithPlaceholder")
void updateAuthorWithPlaceholder(Author author);
//使用Delete註解直接查詢
@Delete("delete from author where id = #{id}")
int deleteAuthor(Author author);
//使用DeleteProvider註解直接查詢
@DeleteProvider(type =AuthorProvider.class,method = "deleteAuthorWithPlaceholder")
int deleteAuthorWithPlaceholder(Author author);
//提供provider註解類的對應方法
class AuthorProvider{
//selectAuthorWithPlaceholder方法
public String selectAuthorWithPlaceholder(Author author){
return new SQL(){{
SELECT("id,username,password,email,bio").FROM("author").WHERE("id=#{id}");
}}.toString();
}
//insertAuthorWithPlaceholder方法
public String insertAuthorWithPlaceholder(Author author){
return new SQL(){{
INSERT_INTO("Author").VALUES("id,username,password,email,bio","#{id},#{username},#{password},#{email},#{bio}");
}}.toString();
}
//updateAuthorWithPlaceholder方法
public String updateAuthorWithPlaceholder(Author author){
return new SQL(){{
UPDATE("Author").SET("id=#{id}","username=#{username}","password=#{password}","email=#{email}","bio=#{bio}").WHERE("id=#{id}");
}}.toString();
}
//deleteAuthorWithPlaceholder方法
public String deleteAuthorWithPlaceholder(Author author){
return new SQL(){{
DELETE_FROM("Author").WHERE("id=#{id}");
}}.toString();
}
}
}
3.3.3、代碼中註解的解析使用示例
//上述提到的輸入流信息我們這裏先以xml爲例子
final String resource = "org/apache/ibatis/builder/Mybatis-config.xml";
final Reader reader = Resources.getResourceAsReader(resource);
//直接代碼構建SqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//初始化全局配置---相當於Mybatis-config.xml中的綁定configuration標籤
Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
//爲全局配置添加需要的Mapper接口---相當於Mybatis-config.xml中的綁定Mapper.xml文件的標籤
configuration.addMapper(AuthorMapperWithAnnotation.class);
//創建SQL會話工廠---利用SqlSessionFactory實現Bean實例化
SqlSessionFactory sqlMapperWithAnnotation = new DefaultSqlSessionFactory(configuration);
//獲取SQL會話鏈接---所有Mybatis的核心API接口
SqlSession sqlSession = sqlMapperWithAnnotation.openSession();
//從會話中獲取綁定好的mapper接口信息---利用反射獲取對應的mapper
AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class);
//執行插入會話---Mybatis核心設計圖中動態代理執行過程(這裏的源碼分析可以參考第四章、第五章)
Author expected = new Author(500, "wolf:\"test\"", "******", "[email protected]", "Something...", null);
mapper.insertAuthorWithPlaceholder(expected);
3.3、核心類和核心方法
- SqlSessionFactoryBuilder:SqlSessionFactory的構造器,可以自己解析配置,也可以通過提前構建好的配置對象構建。
- SqlSessionFactory:管理數據庫會話、聚合Configuration的屬性。
- Configuration:是全局所有的運行數據配置,相當於一個大的倉庫。
- XMLConfigBuilder:解析XML的Configuration構建器。
- XMLMapperBuilder:解析XML的Mapper構建器。
根據時序圖我們可以看到實際上分爲三個過程【創建】-》【解析】-》【構建】
3.3.1、SqlSessionFactoryBuilder源碼分析
public class SqlSessionFactoryBuilder {
//根據字符流構建會話工廠
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
...省略本次無關內容...
//根據字符流和環境和屬性構建會話工廠
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//利用xml配置構建器
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
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 SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
3.3.2、SqlSessionFactory源碼分析
3.3.2.1、SqlSessionFactory源碼分析
/*
*從連接或數據源創建SqlSession
*/
public interface SqlSessionFactory {
//打開會話
SqlSession openSession();
//打開會話是否自動提交
SqlSession openSession(boolean autoCommit);
//打開鏈接的session
SqlSession openSession(Connection connection);
//打開事務隔離級別的session
SqlSession openSession(TransactionIsolationLevel level);
//打開執行器類型的session
SqlSession openSession(ExecutorType execType);
//打開執行器類型,是否自動提交的session
SqlSession openSession(ExecutorType execType, boolean autoCommit);
//打開執行器和事務隔離級別機制的session
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
//打開執行器,鏈接的session
SqlSession openSession(ExecutorType execType, Connection connection);
//獲取全局配置
Configuration getConfiguration();
}
3.3.2.2、DefaultSqlSessionFactory源碼分析
/**
* 默認的會話工廠
* @author Clinton Begin
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//全局配置
private final Configuration configuration;
//構造函數
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
...省略本次無關內容...
}
3.3.3、Configuration源碼分析
/**
* 抽象構建器
* @author Clinton Begin
*/
public abstract class BaseBuilder {
//全局配置
protected final Configuration configuration;
//類型別名註冊器
protected final TypeAliasRegistry typeAliasRegistry;
//類型處理器註冊器
protected final TypeHandlerRegistry typeHandlerRegistry;
//構造函數
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
//獲取全局配置
public Configuration getConfiguration() {
return configuration;
}
//解析正則表達式
protected Pattern parseExpression(String regex, String defaultValue) {
return Pattern.compile(regex == null ? defaultValue : regex);
}
//獲取boolean值
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
//獲取int值
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}
//獲取set值
protected Set<String> stringSetValueOf(String value, String defaultValue) {
value = value == null ? defaultValue : value;
return new HashSet<>(Arrays.asList(value.split(",")));
}
//解析jdbc類型
protected JdbcType resolveJdbcType(String alias) {
if (alias == null) {
return null;
}
try {
return JdbcType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
}
}
//解析結果集類型
protected ResultSetType resolveResultSetType(String alias) {
if (alias == null) {
return null;
}
try {
return ResultSetType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
}
}
//解析參數風格
protected ParameterMode resolveParameterMode(String alias) {
if (alias == null) {
return null;
}
try {
return ParameterMode.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
}
}
//創建實例
protected Object createInstance(String alias) {
Class<?> clazz = resolveClass(alias);
if (clazz == null) {
return null;
}
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new BuilderException("Error creating instance. Cause: " + e, e);
}
}
//根據別名解析類
protected <T> Class<? extends T> 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 TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
if (typeHandlerAlias == null) {
return null;
}
Class<?> type = resolveClass(typeHandlerAlias);
if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
}
@SuppressWarnings("unchecked") // already verified it is a TypeHandler
Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
return resolveTypeHandler(javaType, typeHandlerType);
}
//解析類型處理器
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
if (typeHandlerType == null) {
return null;
}
// javaType ignored for injected handlers see issue #746 for full detail
TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) {
// not in registry, create a new one
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
//解析別名
protected <T> Class<? extends T> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
}
3.3.4.1、XMLConfigBuilder源碼分析
/**
* xml配置構建器
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class XMLConfigBuilder extends BaseBuilder {
//是否解析
private boolean parsed;
//解析器
private final XPathParser parser;
//環境
private String environment;
//本地反射工廠
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
//構造函數
public XMLConfigBuilder(Reader reader) {
this(reader, null, null);
}
//構造函數
public XMLConfigBuilder(Reader reader, String environment) {
this(reader, environment, null);
}
//構造函數
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
//構造函數
public XMLConfigBuilder(InputStream inputStream) {
this(inputStream, null, null);
}
//構造函數
public XMLConfigBuilder(InputStream inputStream, String environment) {
this(inputStream, environment, null);
}
//構造函數
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;
}
//解析全局配置對象
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 {
//issue #117 read properties first
//解析屬性變量節點
propertiesElement(root.evalNode("properties"));
//解析setting節點字段
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加載用戶自定義的VFS解析文件類
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"));
//數據庫ID
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//類型處理器元素
typeHandlerElement(root.evalNode("typeHandlers"));
//mapper接口元素
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
//設置屬性
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
//檢查所有的setting設置是在配置類中已知的
// Check that all settings are known to the configuration class
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).");
}
}
return props;
}
//加載定製vfs解析實現類
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
String value = props.getProperty("vfsImpl");
if (value != null) {
String[] clazzes = value.split(",");
for (String clazz : clazzes) {
if (!clazz.isEmpty()) {
@SuppressWarnings("unchecked")
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
configuration.setVfsImpl(vfsImpl);
}
}
}
}
//加載定製實現類
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
}
//類型別名元素
private void typeAliasesElement(XNode parent) {
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.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
//插件元素
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
//對象工廠元素
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties properties = context.getChildrenAsProperties();
ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(properties);
configuration.setObjectFactory(factory);
}
}
//對象包裝工廠元素
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
configuration.setObjectWrapperFactory(factory);
}
}
//反射工廠元素
private void reflectorFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
configuration.setReflectorFactory(factory);
}
}
//屬性元素
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
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);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
//設置屬性
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
//環境元素
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
//數據庫ID服務元素
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).getDeclaredConstructor().newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
//事務處理的元素
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
//數據源處理的元素
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
//類型處理器的元素
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
//mapper文件解析元素
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
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);
InputStream inputStream = Resources.getResourceAsStream(resource);
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.");
}
}
}
}
}
//是否指定環境
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;
}
}
3.3.4.2、XMLMapperBuilder源碼分析
/**
* XML的mapper接口構建器
*/
public class XMLMapperBuilder extends BaseBuilder {
//解析器
private final XPathParser parser;
//接口助手
private final MapperBuilderAssistant builderAssistant;
//sql節點片段
private final Map<String, XNode> sqlFragments;
//資源
private final String resource;
//不建議使用,未來廢棄
@Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
this(reader, configuration, resource, sqlFragments);
this.builderAssistant.setCurrentNamespace(namespace);
}
//不建議使用,未來廢棄
@Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
//構造函數
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
this(inputStream, configuration, resource, sqlFragments);
this.builderAssistant.setCurrentNamespace(namespace);
}
//構造函數
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
//構造函數
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
//解析mapper
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//將mapper和命名空間綁定
bindMapperForNamespace();
}
//解析等待的結果映射
parsePendingResultMaps();
//解析等待的緩存引用
parsePendingCacheRefs();
//解析等待的會話
parsePendingStatements();
}
//獲取sql片段根據引用ID
public XNode getSqlFragment(String refid) {
return sqlFragments.get(refid);
}
//配置全局配置
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"));
//參數map
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//結果map
resultMapElements(context.evalNodes("/mapper/resultMap"));
//sql
sqlElement(context.evalNodes("/mapper/sql"));
//會話語句
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);
}
}
//構建會話從上下文
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);
}
}
}
//解析等待的結果映射
private void parsePendingResultMaps() {
Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
synchronized (incompleteResultMaps) {
Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
while (iter.hasNext()) {
try {
iter.next().resolve();
iter.remove();
} catch (IncompleteElementException e) {
// ResultMap is still missing a resource...
}
}
}
}
//解析等待的緩存引用
private void parsePendingCacheRefs() {
Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
synchronized (incompleteCacheRefs) {
Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
while (iter.hasNext()) {
try {
iter.next().resolveCacheRef();
iter.remove();
} catch (IncompleteElementException e) {
// Cache ref is still missing a resource...
}
}
}
}
//解析等待的會話
private void parsePendingStatements() {
Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
synchronized (incompleteStatements) {
Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
while (iter.hasNext()) {
try {
iter.next().parseStatementNode();
iter.remove();
} catch (IncompleteElementException e) {
// Statement is still missing a resource...
}
}
}
}
//緩存引用元素
private void cacheRefElement(XNode context) {
if (context != null) {
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
//緩存元素
private void cacheElement(XNode context) {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
//參數映射元素
private void parameterMapElement(List<XNode> list) {
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
Class<?> parameterClass = resolveClass(type);
List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
List<ParameterMapping> parameterMappings = new ArrayList<>();
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
ParameterMode modeEnum = resolveParameterMode(mode);
Class<?> javaTypeClass = resolveClass(javaType);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}
}
//結果映射
private void resultMapElements(List<XNode> list) throws Exception {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
//結果映射
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.emptyList(), null);
}
//結果映射
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
Class<?> typeClass = resolveClass(type);
if (typeClass == null) {
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
//繼承封裝類型
protected Class<?> inheritEnclosingType(XNode resultMapNode, Class<?> enclosingType) {
if ("association".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) {
String property = resultMapNode.getStringAttribute("property");
if (property != null && enclosingType != null) {
MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());
return metaResultType.getSetterType(property);
}
} else if ("case".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) {
return enclosingType;
}
return null;
}
//執行構造元素
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
List<XNode> argChildren = resultChild.getChildren();
for (XNode argChild : argChildren) {
List<ResultFlag> flags = new ArrayList<>();
flags.add(ResultFlag.CONSTRUCTOR);
if ("idArg".equals(argChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
}
}
//執行鑑別器元素
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String typeHandler = context.getStringAttribute("typeHandler");
Class<?> javaTypeClass = resolveClass(javaType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
Map<String, String> discriminatorMap = new HashMap<>();
for (XNode caseChild : context.getChildren()) {
String value = caseChild.getStringAttribute("value");
String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings, resultType));
discriminatorMap.put(value, resultMap);
}
return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
//sql元素
private void sqlElement(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
//sql元素
private void sqlElement(List<XNode> list, String requiredDatabaseId) {
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);
}
}
}
//數據庫ID匹配當前要求
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
if (requiredDatabaseId != null) {
return requiredDatabaseId.equals(databaseId);
}
if (databaseId != null) {
return false;
}
if (!this.sqlFragments.containsKey(id)) {
return true;
}
// skip this fragment if there is a previous one with a not null databaseId
XNode context = this.sqlFragments.get(id);
return context.getStringAttribute("databaseId") == null;
}
//從上下文構建結果映射
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.emptyList(), resultType));
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);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
//執行嵌套結果映射
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) throws Exception {
if ("association".equals(context.getName())
|| "collection".equals(context.getName())
|| "case".equals(context.getName())) {
if (context.getStringAttribute("select") == null) {
validateCollection(context, enclosingType);
ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
return resultMap.getId();
}
}
return null;
}
//校驗集合
protected void validateCollection(XNode context, Class<?> enclosingType) {
if ("collection".equals(context.getName()) && context.getStringAttribute("resultMap") == null
&& context.getStringAttribute("javaType") == null) {
MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());
String property = context.getStringAttribute("property");
if (!metaResultType.hasSetter(property)) {
throw new BuilderException(
"Ambiguous collection type for property '" + property + "'. You must specify 'javaType' or 'resultMap'.");
}
}
}
//構建命名空間的mapper
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
}
Mybatis構建加載初始化源碼分析這節到這裏就結束了,可以再回顧下核心重點圖。