源碼分析環(huán)境搭建贱案,我們將從mybatis 的原生api 開始分析头岔,作為分析源碼的入口。
1. 環(huán)境搭建
github: https://github.com/yoxiyoxiiii/boot-mybatis
2. 查詢分析 (mapper.xml 可用逆向工程生成吧寺,重點不是這個)
@Test
public void selectOne() throws IOException {
String config = "config.xml";
InputStream inputStream = Resources.getResourceAsStream(config);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey", 1);
System.out.println(user);
sqlSession.close();
}
這里基本上的每句代碼都有作用梗逮,我們姑且先從第一感覺 selectOne() 這個方法開始。
User user = sqlSession.selectOne("com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey", 1);
sqlSession.selectOne() 方法 兩個參數(shù):
第一個:com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey 對應mapper 接口中的方法诬垂。
第二個: 表示 查詢參數(shù)劲室,也就是查詢 id = 1 的user 對象。
2.1 sqlSession.selectOne()
public interface SqlSession extends Closeable {
/**
* Retrieve a single row mapped from the statement key.
* @param <T> the returned object type
* @param statement
* @return Mapped object
*/
<T> T selectOne(String statement);
/**
* Retrieve a single row mapped from the statement key and parameter.
* @param <T> the returned object type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Mapped object
*/
<T> T selectOne(String statement, Object parameter);
/**
* Retrieve a list of mapped objects from the statement key and parameter.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @return List of mapped object
*/
<E> List<E> selectList(String statement);
/**
* Retrieve a list of mapped objects from the statement key and parameter.
* @param <E> the returned list element type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return List of mapped object
*/
<E> List<E> selectList(String statement, Object parameter);
..... 省略其他方法结窘。
}
點進去我們就會發(fā)現(xiàn) SqlSession 接口申明了 這些方法很洋,我們來看一下接口的實現(xiàn)有哪些:
上圖我們sqlSession看到有三個 實現(xiàn)類,那我們在這里用的哪一個呢隧枫?最簡單的辦法就 debug 一下喉磁,可以看出來是 DefaultSqlSession 谓苟,然后我們就來看看 為什么是 DefaultSqlSession 而不是其他。這里就要從前面幾句代碼找答案了线定。
String config = "config.xml";
InputStream inputStream = Resources.getResourceAsStream(config); //這里是讀取配置文件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);// 這句話就是關鍵
SqlSession sqlSession = sqlSessionFactory.openSession();
2.2: SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
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(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
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(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
// SqlSession sqlSession = sqlSessionFactory.openSession(); 這句話就會調(diào)這里,
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
// 創(chuàng)建 DefaultSqlSession 對象斤讥,這里就解釋了為什么是DefaultSqlSession 而不是其他的實現(xiàn)類纱皆。
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();
}
}
2.3 回到 selectOne 方法。
在知道實現(xiàn)類以后我們就來看一下 DefaultSqlSession 中的 selectOne() 方法:
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many. 復用 selectList()方法芭商。
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) { //判斷集合派草,返回一條
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//最后會到這里
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {// 從配置信息里面獲取 sql 映射的對象 MappedStatement 暫時可以暴力的理解為 從 mapper.xml 中獲取用執(zhí)行的sql 語句,其實不僅僅是這樣的铛楣。
// statement = com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey
MappedStatement ms = configuration.getMappedStatement(statement);
// 執(zhí)行查詢近迁,查詢動作交給了 executor 對象。
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
executor.query(ms, wrapCollection(parameter) 執(zhí)行查詢簸州,返回結(jié)果鉴竭,后面我們在來分析 executor 的過程。
我們來騷味 總結(jié)一下 岸浑,到這里我們都分析出哪些東西:
1:我們知道 sqlSession 具體接口對象 是 DefaultSqlSession
2:具體的 查詢?nèi)蝿?sql 語句執(zhí)行 是 交給了一個 叫 executor 的對象搏存。
3:用到的設計模式:工作模式 SqlSessionFactory