sql語句的執行流程
static {
InputStream inputStream = MybatisTest.class.getClassLoader().getResourceAsStream("mybatis-configuration.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 查詢單個記錄
*/
@Test
public void testSelectOne() {
SqlSession session = sqlSessionFactory.openSession();
User user = session.selectOne(NAME_SPACE + ".selectUserById", 1);
System.out.println(user);
session.close();
}
上面是一個簡單的查詢操作,我們看源碼,通過配置文件產生SqlSessionFactory,追溯源碼可以發現其實現是 DefaultSqlSessionFactory。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//Environment對象封裝了配置文件中對於數據源和事務的配置
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//獲取Executor對象,用來執行sql語句
final Executor executor = configuration.newExecutor(tx, execType);
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();
}
}
重點看怎麼獲取session也就是opensession方法,再重點則是看15行獲取executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
可以發現,實際上是看我們的配置生成不同策略的執行器對象,如緩存enablecach
- insert操作流程
@Override
2 public int insert(String statement, Object parameter) {
3 return update(statement, parameter);
4 }
我們看到調用的insert確實用的update方法
1 public int update(String statement, Object parameter) {
2 try {
3 dirty = true;
4 MappedStatement ms = configuration.getMappedStatement(statement);
5 return executor.update(ms, wrapCollection(parameter));
6 } catch (Exception e) {
7 throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
8 } finally {
9 ErrorContext.instance().reset();
10 }
11 }
看到第四行,他將利用statement參數得到MappedStatement對象
而在第五行,他通過執行器的update方法,傳入了一個wrapConnection方法返回的結果,看一下這個方法
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
DefaultSqlSession.StrictMap<Object> map = new DefaultSqlSession.StrictMap<Object>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
DefaultSqlSession.StrictMap<Object> map = new DefaultSqlSession.StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
這裏的parameter正是我們此時準備insert/update的對象,他會先對他進行類型的判斷,根據是Collection還是array數組類型放到一個map內,他的目的就是把我們的目標對象先找個地方存起來而已
注意:這裏的 StrictMap ,其實就是一個 HashMap。他繼承了hashmap加了一點判斷而已
Executor 對象上面我們已經介紹了,由於默認是開啓一級緩存的,這時候我們進入 CachingExecutor 類的 update() 方法:
1 public int update(MappedStatement ms, Object parameterObject) throws SQLException {
2 flushCacheIfRequired(ms);
3 return delegate.update(ms, parameterObject);
4 }
看名字就知道第二行這個和緩存有關係,意思是是否清楚緩存
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
我們發現他主要是看通過看我們是否配置了標籤或者在語句裏面是否寫了flushcache=true來斷定的,有的話,這次操作做之前就要清除一次
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
這裏面,第六行就是傳統的jdbc預編譯的過程,預編譯完成後返回這個statement
1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
2 Statement stmt;
3 Connection connection = getConnection(statementLog);
4 stmt = handler.prepare(connection, transaction.getTimeout());
5 handler.parameterize(stmt);
6 return stmt;
7 }
再看hanler.update方法
1 public int update(Statement statement) throws SQLException {
2 String sql = boundSql.getSql();
3 Object parameterObject = boundSql.getParameterObject();
4 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
5 int rows;
6 if (keyGenerator instanceof Jdbc3KeyGenerator) {
7 statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
8 rows = statement.getUpdateCount();
9 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
10 } else if (keyGenerator instanceof SelectKeyGenerator) {
11 statement.execute(sql);
12 rows = statement.getUpdateCount();
13 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
14 } else {
15 statement.execute(sql);
16 rows = statement.getUpdateCount();
17 }
18 return rows;
19 }
這裏面就是我們的jdbc操作,拿到sql語句,以及對應需要填充的參數,根據不同的keygenerator生成主鍵
update和delete應該都是差不多的,不再研究
- select操作
1 public <T> T selectOne(String statement, Object parameter) {
2 // Popular vote was to return null on 0 results and throw exception on too many.
3 List<T> list = this.<T>selectList(statement, parameter);
4 if (list.size() == 1) {
5 return list.get(0);
6 } else if (list.size() > 1) {
7 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
8 } else {
9 return null;
10 }
11 }
我們發現selectOne和selectList本質都是調用selectList,如果selectOne又得到了多個結果,將拋出異常
1 @Override
2 public <E> List<E> selectList(String statement, Object parameter) {
3 return this.selectList(statement, parameter, RowBounds.DEFAULT);
4 }
5
6 @Override
7 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
8 try {
9 MappedStatement ms = configuration.getMappedStatement(statement);
10 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
11 } catch (Exception e) {
12 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
13 } finally {
14 ErrorContext.instance().reset();
15 }
16 }
暫時拋開MappedStatement,我們看看這裏的執行器調用query的過程
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
2 BoundSql boundSql = ms.getBoundSql(parameterObject);
3 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
4 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
5 }
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
2 throws SQLException {
3 Cache cache = ms.getCache();
4 if (cache != null) {
5 flushCacheIfRequired(ms);
6 if (ms.isUseCache() && resultHandler == null) {
7 ensureNoOutParams(ms, parameterObject, boundSql);
8 @SuppressWarnings("unchecked")
9 List<E> list = (List<E>) tcm.getObject(cache, key);
10 if (list == null) {
11 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
12 tcm.putObject(cache, key, list); // issue #578 and #116
13 }
14 return list;
15 }
16 }
17 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
18 }
解析ResultSet的主要代碼如下:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//結果集的第一個結果(一般也就只有一個結果集)
ResultSetWrapper rsw = getFirstResultSet(stmt);
//獲取結果映射(自己在mapper裏面配置的,一般也只有一個)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
//循環次數爲有多少個resultMap
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//根據我們定義的這個resultMap處理rsw生成java對象
handleResultSet(rsw, resultMap, multipleResults, null);
//獲取結果集的下一個結果
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
//和resultMaps的遍歷處理類似
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//將查詢到的每個字段和Bean實體中的屬性對應起來,生成一個Result對象放到Handler裏面的ResultList內
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//結果映射對象及值添加到multipleResults中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}