編程式使用mybatis一般會經過SqlSessionFactoty拿到SqlSession接口對象,以下所示:java
String resource = "mybatis.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); } finally { session.close(); }
SqlSession中封裝了各類訪問數據庫的情形,下面咱們按照上面的例子來探尋下執行一條sql語句過程,其中mapper.xml文件映射以下:sql
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper>
openSession()最終調用的是下面代碼:數據庫
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(); } }
其主要過程就是初始化事務以及執行器Executor,前面咱們分析過默認使用的是帶有緩存功能的CachingExecutor。下面咱們看下 Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);是如何執行:編程
selectOne最終調用的是selectList語句執行:緩存
@Override public <T> T selectOne(String statement) { return this.<T>selectOne(statement, null); } @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; } }
若是查詢出多個結果就會報錯,這就是爲何咱們常常使用load接口獲取一條記錄的同時若是數據庫有兩條記錄知足條件就會包sql語句執行錯誤。session
selectList語句執行過程以下:mybatis
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(); } }
首先根據statement從Configuration中獲取MappedStatement對象,MappedStatement封裝了<select>節點下的信息,接着調用executor的query方法執行並獲取到結果信息返回,下面咱們深刻到executor中的query方法中看看是如何執行的,具體的execuator是SimpleExecutor。SimpleExecutor直接執行了Executor接口,其query方法以下:app
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); }
首先調用MappedStatement內部方法獲取BoundSql對象,內部方法以下:ide
public BoundSql getBoundSql(Object parameterObject) { BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } // check for nested result maps in parameter mappings (issue #30) for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql; }
首先根據參數對象利用SqlSource獲取Boundsql,咱們知道若是不是動態sql語句SqlSource的實現爲RawSqlSource,RawSqlSource並無給出具體的Boundsql,其默認調用的是StaticSqlSource即SqlSource真正的實現是StaticSqlSource。咱們能夠從下面代碼看出:fetch
public class RawSqlSource implements SqlSource { private final SqlSource sqlSource; public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) { this(configuration, getSql(configuration, rootSqlNode), parameterType); } public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) { SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> clazz = parameterType == null ? Object.class : parameterType; //實際得到sqlSource過程 sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>()); } private static String getSql(Configuration configuration, SqlNode rootSqlNode) { DynamicContext context = new DynamicContext(configuration, null); rootSqlNode.apply(context); return context.getSql(); } @Override public BoundSql getBoundSql(Object parameterObject) { //默認初始化了sqlSource return sqlSource.getBoundSql(parameterObject); } }
實際得到sqlSource是經過sqlSourceParser.parse解析獲得,以下:
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); String sql = parser.parse(originalSql); return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
能夠看到最終的sqlSource是StaticSqlSource,StaticSqlSource只是一個簡單的實現就是new一個BoundSql返回。
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
這句代碼獲取到了參數映射ParameterMapping,若是parameterMappings不存在直接從parameterMap中拿到ParameterMapping並從新new一個BoundSql語句。
繼續回到query方法中,獲取到了BoundSql對象後接着建立
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);建立一級緩存key,接着執行另外一條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++; //resultHandler 爲nll(傳進來就是null)首先從局部緩存中拿數據 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; //若是緩存數據不爲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; }
查詢數據庫代碼以下:
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是個抽象方法具體實如今SimpleExecutor中以下:
@Override 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 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //準備JDBC statement stmt = prepareStatement(handler, ms.getStatementLog()); //處理查詢 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
StatementHandler使用的是RoutingStatementHandler來路由StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
接着執行return handler.<E>query(stmt, resultHandler);實如今RoutingStatementHandler中
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); }
可是不是由RoutingStatementHandler直接執行,而是由delegate執行query,RoutingStatementHandler根據StatementType來路由StatementHandler,若是沒有執行statementType默認爲PREPARED,則delegate爲PreparedStatementHandler,故query語句爲:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
接口就是標準的sql語句執行過程,執行完經過resultSetHandler處理執行結果,這裏的ResultSetHandler是DefaultResultSetHandler,其代碼以下:
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { 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); }
這部分代碼主要是處理結果映射關係比較複製暫時不作詳細分析。
到此一條sql語句執行過程分析完畢。