準備工作:github下載mybatis源碼 IDEA 2017.3.5;
本文通過單步調試AutoConstructorTest.java開始閱讀源碼
1 AutoConstructorTest.java如圖:
package org.apache.ibatis.autoconstructor;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.Reader;
import java.sql.Connection;
import java.util.List;
public class AutoConstructorTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void setUp() throws Exception {
// create a SqlSessionFactory
//獲取mybatis-config.xml的輸入流
final Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
//獲取session
// populate in-memory database
final SqlSession session = sqlSessionFactory.openSession();
//獲取session連接
final Connection conn = session.getConnection();
//動態獲取絕對路徑下的sql腳本輸入流
final Reader dbReader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/CreateDB.sql");
//通過ScriptRunner運行sql腳本,創建數據庫表
final ScriptRunner runner = new ScriptRunner(conn);
runner.setLogWriter(null);
runner.runScript(dbReader);
conn.close();
dbReader.close();
session.close();
}
@Test
public void fullyPopulatedSubject() {
final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
final Object subject = mapper.getSubject(1);
Assert.assertNotNull(subject);
} finally {
sqlSession.close();
}
}
@Test(expected = PersistenceException.class)
public void primitiveSubjects() {
final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
mapper.getSubjects();
} finally {
sqlSession.close();
}
}
@Test
public void wrapperSubject() {
final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
verifySubjects(mapper.getWrapperSubjects());
} finally {
sqlSession.close();
}
}
@Test
public void annotatedSubject() {
final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
verifySubjects(mapper.getAnnotatedSubjects());
} finally {
sqlSession.close();
}
}
@Test(expected = PersistenceException.class)
public void badSubject() {
final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
mapper.getBadSubjects();
} finally {
sqlSession.close();
}
}
private void verifySubjects(final List<?> subjects) {
Assert.assertNotNull(subjects);
Assertions.assertThat(subjects.size()).isEqualTo(3);
}
}
@BeforeClass
public static void setUp() throws Exception {
// create a SqlSessionFactory
//1.1 獲取絕對路徑下面的mybatis-config配置文件
final Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
//1.2 獲取sqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// populate in-memory database
final SqlSession session = sqlSessionFactory.openSession();
final Connection conn = session.getConnection();
final Reader dbReader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/CreateDB.sql");
final ScriptRunner runner = new ScriptRunner(conn);
runner.setLogWriter(null);
runner.runScript(dbReader);
conn.close();
dbReader.close();
session.close();
}
SqlSessionFactoryBuilder獲取SqlSessionFactory:
build方法:
//reader等於上面的reader, environment,Properties爲null
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//1.3 這裏parser獲取configuration,重點解析怎樣獲取configuration
//然後根據build方法獲取SqlSessionFactory
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.
}
}
}
1.4 parse.parse():
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//解析mybatis-config.xml文件的configuration根節點
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析並設置properties
propertiesElement(root.evalNode("properties"));
//解析並獲取setttings
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//解析並獲取typeAliases
typeAliasesElement(root.evalNode("typeAliases"));
//解析並獲取plugins
pluginElement(root.evalNode("plugins"));
////解析並獲取objectFactory
objectFactoryElement(root.evalNode("objectFactory"));
//解析並獲取objectWrapperFactory
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析並獲取reflectorFactory
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//settingsElement 設置默認的Configuration靜態屬性如設置是否使用緩存等
//configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析environments節點並配置數據源
environmentsElement(root.evalNode("environments"));
//解析environments節點並配置數據源提供者(即mysql,oracle)
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers節點並配置類型解析器
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers節點並配置mappers
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析enviromentsElement配置數據源代碼如下
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());
}
}
}
}
至此,sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 獲取sqlSessionFactory完畢!
2 2 mybatis-config.xml :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- autoMappingBehavior should be set in each test case -->
<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>
<mappers>
<mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml"/>
</mappers>
</configuration>
3 openSession獲取session解析:
至此,獲取Session完畢!
4 獲取Connection
DefaultSqlSession.getConnection()
//獲取session連接
final Connection conn = session.getConnection();
@Override
public Connection getConnection() {
try {
return executor.getTransaction().getConnection();
} catch (SQLException e) {
throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e);
}
}
4.1 session.getConnection():
4.2 JdbcTransaction.getConnection():
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
openConnection:
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
然後,調用 dataSource.getConnection();
然後,調用 UnpooledDataSource.doGetConenction:
UnpooledDataSource:
每個數據庫建立一個session連接:
public class UnpooledDataSource implements DataSource {
private ClassLoader driverClassLoader;
private Properties driverProperties;
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();
private String driver;
private String url;
private String username;
private String password;
private Boolean autoCommit;
private Integer defaultTransactionIsolationLevel;
......
}
然後,調用doGetConnection
private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties();
if (driverProperties != null) {
props.putAll(driverProperties);
}
if (username != null) {
props.setProperty("user", username);
}
if (password != null) {
props.setProperty("password", password);
}
return doGetConnection(props);
}
doGetConnection:
private Connection doGetConnection(Properties properties) throws SQLException {
//初始化驅動
initializeDriver();
//獲取connection
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
然後,調用initializerDriver:
private synchronized void initializeDriver() throws SQLException {
//根據CurrentHashMap類型的registeredDrivers判斷是否已註冊當前驅動,如果沒有,進行註冊,如果已經註冊,則跳出此函數
//private static Map<String, Driver> registeredDrivers = new //ConcurrentHashMap<String, Driver>();
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
//根據類加載器獲取驅動類型 Class<?>
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
//創建驅動類實例
Driver driverInstance = (Driver)driverType.newInstance();
//創建驅動代理對象並註冊驅動
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
至此,初始化驅動完畢!
接着獲取connection並配置connection返回
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
至此,已經獲取到connection連接