public class MybatisTests { SqlSessionFactory factory; @Before public void loadConfig() { try (InputStream inputStream = Resources.getResourceAsStream("mybatis2.xml")) { factory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } @Test public void testInsert2() { SqlSession session = factory.openSession(); User user = session.selectOne("findUserById", 1); System.out.println(user); session.close(); } }
經過跟蹤testInsert2()方法去查看mybatis內部的sql執行過程。sql
DefaultSqlSessionFactory的重載方法中支持傳入autoCommit(是否自動提交),transactionIsolationLevel(事務隔離級別),ExecutorType(sql執行器類型:SIMPLE, REUSE, BATCH),Connection 等來建立SqlSession對象。數據庫
事務隔離級別:緩存
public enum TransactionIsolationLevel { NONE(Connection.TRANSACTION_NONE), READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED), READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED), REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ), SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE); }
進入openSession()方法,實際上執行的方法是factory.openSessionFromDataSource()方法:session
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); 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(); } }
@Override public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.<T>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; } }
繼續跟蹤,進入的是DefaultSqlSession.selectList()方法:mybatis
@Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); 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(); } }
先根據key從Configuration中獲取到MappedStatement對象。而後執行excutor.query()方法。app
先跟蹤wrapCollection(parameter)
方法:ide
private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap<Object> map = new StrictMap<Object>(); map.put("collection", object); if (object instanceof List) { map.put("list", object); } return map; } else if (object != null && object.getClass().isArray()) { StrictMap<Object> map = new StrictMap<Object>(); map.put("array", object); return map; } return object; }
wrapCollection(parameter)
方法就是在處理集合類型的參數:若是Collection對象,就放到一個Map對象中,key爲"collection",若是是list對象,key爲"list",若是是array對象,key爲"array".不然就是本來的對象。(這裏也就能理解以前在mapper.xml中傳參爲集合的時候爲何名稱必定要是collectin,list之類的了)。源碼分析
跟蹤excutor.query()
方法:(sqlSession委託executor去真正執行數據庫的增刪改查操做),這裏進入的是CachingExecutor
實現類。在建立sqlSession的時候從源碼分析中能夠知道默認使用的是CachingExecutor實現類。測試
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
getBoundSql()方法負責構造sql語句和輸入參數。 fetch
BoundSql對象中已經將sql轉爲了?形式,而且parameterMappings中放了sql入參的各類屬性,parameterObject放的是真正的參數對象。
createCacheKey()方法先略過。
繼續跟蹤query方法:
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
在這個方法中,先去查詢二級緩存,若是二級緩存中沒有數據,再進入query方法(委託給BaseExector去繼續處理)。
也就是說CachingExecutor只負責處理二級緩存,若是沒有二級緩存,則委託給BaseExector處理。
再進去query()方法:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
localCache
這個對象負責的是一級緩存,若是一級緩存中沒有數據,就進入queryFromDatabase()
方法去數據庫中查詢。
跟蹤queryFromDatabase()
方法:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
這個方法內進行了防止緩存穿透的防範。(先在緩存中存儲一個佔位符,這個緩存的數據主要是爲了在查詢的過程當中,對外提供緩存數據,以此來保護數據庫。)
跟蹤doQuery()
方法:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
跟蹤newStatementHandler()方法,發現這裏面實際上是在構造PreparedStatementHandler或者StatementHandler對象。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
跟蹤prepareStatement()方法
,發現裏面就是在構造prepareStatement對象或者Statement對象併爲其設置參數。 而後後面的query()方法就是直接調用JDBC的代碼了。