1. 準備工作
? 編寫測試代碼(具體請參考《Mybatis入門示例》),設置斷點,以Debug模式運行,具體代碼如下:?
String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
???
2.源碼分析
我們此次就對上面的代碼進行跟蹤和分析,let's go。
首先我們按照順序先看看第一行和第二行代碼,看看它主要完成什么事情:
String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
????
讀取Mybaits的主配置配置文件,并返回該文件的輸入流,我們知道Mybatis所有的SQL語句都寫在XML配置文件里面,所以第一步就需要讀取這些XML配置文件,這個不難理解,關鍵是讀取文件后怎么存放。
?
我們接著看第三行代碼(如下),該代碼主要是讀取配置文件流并將這些配置信息存放到Configuration類中。
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
?
????
SqlSessionFactoryBuilder的build的方法如下:
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
??
其實是調用該類的另一個build方法來執行的,具體代碼如下:
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.
}
}
}
??
我們重點看一下里面兩行:
//創建一個配置文件流的解析對象XMLConfigBuilder,其實這里是將環境和配置文件流賦予解析類
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 解析類對配置文件進行解析并將解析的內容存放到Configuration對象中,并返回SqlSessionFactory
return build(parser.parse());
??
這里的XMLConfigBuilder初始化其實調用的代碼如下:?
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;
}
?
?
?XMLConfigBuilder的parse方法執行代碼如下:??
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each MapperConfigParser can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
?
解析的內容主要是在parseConfiguration方法中,它主要完成的工作是讀取配置文件的各個節點,然后將這些數據映射到內存配置對象Configuration中,我們看一下parseConfiguration方法內容:?
private void parseConfiguration(XNode root) {
try {
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
propertiesElement(root.evalNode("properties"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
最后的build方法其實是傳入配置對象進去,創建DefaultSqlSessionFactory實例出來. DefaultSqlSessionFactory是SqlSessionFactory的默認實現.?
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
???
?最后我們看一下第四行代碼:?
SqlSession session = ssf.openSession();
?
通過調用DefaultSqlSessionFactory的openSession方法返回一個SqlSession實例,我們看一下具體是怎么得到一個SqlSession實例的。首先調用openSessionFromDataSource方法。??
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
?
?下面我們看一下openSessionFromDataSource方法的邏輯:??
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Connection connection = null;
try {
//獲取配置信息里面的環境信息,這些環境信息都是包括使用哪種數據庫,連接數據庫的信息,事務
final Environment environment = configuration.getEnvironment();
//根據環境信息關于數據庫的配置獲取數據源
final DataSource dataSource = getDataSourceFromEnvironment(environment);
//根據環境信息關于事務的配置獲取事務工廠
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
connection = dataSource.getConnection();
if (level != null) {
//設置連接的事務隔離級別
connection.setTransactionIsolation(level.getLevel());
}
//對connection進行包裝,使連接具備日志功能,這里用的是代理。
connection = wrapConnection(connection);
//從事務工廠獲取一個事務實例
Transaction tx = transactionFactory.newTransaction(connection, autoCommit);
//從配置信息中獲取一個執行器實例
Executor executor = configuration.newExecutor(tx, execType);
//返回SqlSession的一個默認實例
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeConnection(connection);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
?
傳入參數說明:
(1)ExecutorType:執行類型,ExecutorType主要有三種類型:SIMPLE, REUSE, BATCH,默認是SIMPLE,都在枚舉類ExecutorType里面。
(2)TransactionIsolationLevel:事務隔離級別,都在枚舉類TransactionIsolationLevel中定義。
(3)autoCommit:是否自動提交,主要是事務提交的設置。
?
?DefaultSqlSession是SqlSession的實現類,該類主要提供操作數據庫的方法給開發人員使用。
??
這里總結一下上面的過程,總共由三個步驟:
步驟一:讀取Ibatis的主配置文件,并將文件讀成文件流形式(InputStream)。
?
步驟二:從主配置文件流中讀取文件的各個節點信息并存放到Configuration對象中。讀取mappers節點的引用文件,并將這些文件的各個節點信息存放到Configuration對象。
?
步驟三:根據Configuration對象的信息獲取數據庫連接,并設置連接的事務隔離級別等信息,將經過包裝數據庫連接對象SqlSession接口返回,DefaultSqlSession是SqlSession的實現類,所以這里返回的是DefaultSqlSession,SqlSession接口里面就是對外提供的各種數據庫操作。