SqlSession的建立過程
- 概述
- XPath方式解析XML檔案
- Configuration執行個體建立過程
- XMLConfigBuilder
-
- XMLConfigBuilder#parse
- SqlSession執行個體建立過程
- SqlSessionFactoryBuilder
-
- SqlSessionFactoryBuilder#build
- DefaultSqlSessionFactory
-
- DefaultSqlSessionFactory#openSession
- DefaultSqlSession
-
- DefaultSqlSession#getMapper
概述
SqlSession對象 表示 MyBaits架構 與 資料庫 建立的會話
可以通過SqlSession執行個體 完成 對資料庫的增删改查操作
為了簡化流程描述,将SqlSession的建立過程拆解為3個階段
- Configuration執行個體的建立過程
- SqlSessionFactory執行個體的建立過程
- SqlSession執行個體化的過程
XPath方式解析XML檔案
MyBatis的主配置檔案和Mapper配置都使用的是XML格式
MyBatis中的Configuration元件 用于 描述 主配置檔案資訊,架構 在啟動時 會解析 XML配置,将 配置資訊 轉換為 Configuration對象
JDK API中提供了3種方式解析XML,分别為DOM、SAX和XPath
在這3種方式中,API最易于使用的就是XPath方式,MyBatis架構中也采用XPath方式解析XML檔案中的配置資訊
為了簡化XPath解析操作,MyBatis通過XPathParser工具類 封裝了 對XML的解析操作
同時使用XNode類增強了 對XML節點的操作
使用XNode對象,可以很友善地擷取節點的屬性、子節點等資訊
Configuration執行個體建立過程
Configuration是MyBatis中比較重要的元件,主要有以下3個作用:
- 用于描述MyBatis配置資訊,例如<settings>标簽配置的參數資訊
- 作為容器 注冊(動詞) MyBatis其他元件,例如TypeHandler、MappedStatement等
- 提供工廠方法,建立ResultSetHandler、StatementHandler、Executor、ParameterHandler等元件執行個體
在SqlSession執行個體化前,首先解析 MyBatis主配置檔案 及 所有Mapper檔案,建立Configuration執行個體
MyBatis通過XMLConfigBuilder類 完成Configuration執行個體的建構工作
XMLConfigBuilder
XMLConfigBuilder繼承了BaseBuilder,BaseBuilder中有Configuration成員變量
XMLConfigBuilder有許多構造方法,主要是兩類,一類是位元組流讀取,一類是字元讀取
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
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;
}
接下來通過
parse
方法傳回Configuration執行個體
XMLConfigBuilder#parse
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"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 這裡把解析後的root節點各種資訊都set in Configuration屬性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// set mapper資訊inConfiguration屬性中
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
MyBatis主配置檔案中所有标簽的用途如下
- <properties>:用于配置屬性資訊,這些屬性的值可以通過${…}方式引用
- <settings>:通過一些屬性來控制MyBatis運作時的一些行為。例如,指定日志實作、預設的Executor類型等
- <typeAliases>:用于配置類型别名,目的是為Java類型設定一個更短的名字。它存在的意義僅在于用來減少類完全限定名的備援
- <plugins>:用于注冊使用者自定義的插件資訊
- <objectFactory>:MyBatis 通過 對象工廠(ObjectFactory)建立 參數對象 和 結果集映射對象,預設的對象工廠需要 做的僅僅是 執行個體化目标類,要麼通過預設構造方法,要麼 在參數映射存在的時候 通過 參數構造方法來執行個體化
- <objectWrapperFactory>:MyBatis通過ObjectWrapperFactory建立ObjectWrapper對象,通過ObjectWrapper對象能夠很友善地擷取對象的屬性、方法名等反射資訊。标簽用于配置使用者自定義的ObjectWrapperFactory
- <reflectorFactory>:MyBatis通過反射工廠(ReflectorFactory)建立 描述Java類型反射資訊 的 Reflector對象,通過Reflector對象能夠很友善地擷取Class對象的Setter/Getter方法、屬性等資訊。标簽用于配置自定義的反射工廠
- <environments>:用于配置MyBatis資料連接配接相關的環境及事務管理器資訊。通過該标簽可以配置多個環境資訊,然後指定具體使用哪個
- <databaseIdProvider>:MyBatis能夠根據不同的資料庫廠商執行不同的SQL語句,該标簽用于配置資料庫廠商資訊
- <typeHandlers>:用于注冊使用者自定義的類型處理器(TypeHandler)
- <mappers>:用于配置MyBatis Mapper資訊
MyBatis架構啟動後,首先建立Configuration執行個體,然後解析所有配置資訊,将解析後的配置資訊存放在Configuration執行個體中
注意:Configuration類中有mapperRegistry成員變量,mapperRegistry 會在後面執行Mapper過程中用到
SqlSession執行個體建立過程
XMLConfigBuilder#parse方法是被SqlSessionFactoryBuilder類在SqlSessionFactoryBuilder#build中調用的
SqlSessionFactoryBuilder#build傳回一個SqlSessionFactory執行個體
然後SqlSessionFactory執行個體的openSession()方法建立一個SqlSession執行個體
MyBatis中的SqlSession執行個體使用工廠模式建立
在建立SqlSession執行個體 之前 需要 先建立 SqlSessionFactory工廠執行個體
然後調用SqlSessionFactory執行個體的openSession()方法,例如下面代碼:
Reader reader = Resources.getResourceAsReader("xx/xx/xx/mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
Configuration configuration = factory.getConfiguration();
為了建立SqlSessionFactory執行個體,首先建立了一個SqlSessionFactoryBuilder執行個體
以MyBatis主配置檔案輸入流作為參數,調用SqlSessionFactoryBuilder執行個體的build()方法
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder#build
build
方法也主要有兩類,一類是字元流,一類是位元組
build方法中先構造XMLConfigBuilder執行個體
然後通過XMLConfigBuilder#parse方法得到一個Configuration執行個體
再使用build方法通過Configuration執行個體 得到一個 SqlSessionFactory執行個體
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
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);
}
SqlSessionFactory接口隻有一個預設的實作,即DefaultSqlSessionFactory
在上面的代碼中,重載的build()方法中以Configuration執行個體作為參數,通過new關鍵字建立了一個DefaultSqlSessionFactory執行個體
DefaultSqlSessionFactory類中有各種openSession方法,可以得到一個SqlSession執行個體
DefaultSqlSessionFactory
DefaultSqlSessionFactory#openSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 擷取Mybatis主配置檔案 配置的 環境資訊
final Environment environment = configuration.getEnvironment();
// 建立 事務管理器工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 建立 事務管理器
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根據 Mybatis主配置檔案中 制定的ExecutorType 建立對應的 Executor 執行個體
final Executor executor = configuration.newExecutor(tx, execType);
// 建立 DefaultSqlSession執行個體
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
MyBatis提供了兩種事務管理器,分别為JdbcTransaction和ManagedTransaction
JdbcTransaction 是使用 JDBC中的Connection對象 實作 事務管理的
ManagedTransaction表示 事務 由外部容器 管理
這兩種事務管理器分别由對應的工廠類JdbcTransactionFactory和ManagedTransactionFactory建立
事務管理器對象建立完畢後,接着調用Configuration對象的newExecutor()方法,根據MyBatis主配置檔案中指定的Executor類型建立對應的Executor對象
最後以Executor對象和Configuration對象作為參數,通過Java中的new關鍵字建立一個DefaultSqlSession對象
DefaultSqlSession對象中持有Executor對象的引用,真正執行SQL操作的是Executor對象
DefaultSqlSession
DefaultSqlSession#getMapper
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
getMapper的建立及執行過程就是接下來要整理的内容