Mybatis工作原理(含部分源碼)
阿新 • • 發佈:2018-07-24
context off params 判斷 new trace app name res
MyBatis的初始化
1、讀取配置文件,形成InputStream
String resource = "mybatis.xml";
// 加載mybatis的配置文件(它也加載關聯的映射文件)
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
2、解析XML配置文件,創建SqlSessionFacotry
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // SqlSessionFactoryBuilder類 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. } } }
根據Configuration
對象來創建SqlSession
MyBatis的SQL查詢流程
創建SqlSession
sqlSession = sessionFactory.openSession(); User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1); // DefaultSqlSession類 public <T> T selectOne(String statement, Object parameter) { // 使用的selectList List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { // 多於1個結果時拋出異常 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; // list.size() == 0 } } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 根據mapper.xml文件中的某個SQL語句創建MappedStatement 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(); } }
執行器在query()
方法中,先查詢緩存判斷是否命中,命中則直接返回,否則從數據庫中查詢。
// CachingExecutor類 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 將參數與mapper中的sql合並 BoundSql boundSql = ms.getBoundSql(parameterObject); // 創建緩存的key CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } // BaseExecutor類,創建緩存對象 @Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); // mapper文件中的id cacheKey.update(rowBounds.getOffset()); // 分頁偏移 cacheKey.update(rowBounds.getLimit()); // 每頁的大小 cacheKey.update(boundSql.getSql()); // sql語句 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // 模仿 DefaultParameterHandler 邏輯,記錄每個參數 for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); // 每個SqlSessionFacotry的id } return cacheKey; } // CachingExecutor類 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, parameterObject, boundSql); @SuppressWarnings("unchecked") /** * PerpetualCache是默認二級緩存實現類 * Map<Object, Object> cache = new HashMap<Object, Object>(); map的key就是CacheKey key * CacheKey中有個hashcode = multiplier * hashcode + 每個update(Object object)object的hashCode() * update()方法會向updateList添加元素 * CacheKey重寫的equals()方法中先判斷hashcode是否相等 * 然後用updateList每個對象的equals()判斷 * 這兩個條件都滿足就說明緩存命中,cache.get(key)也就有值 */ List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { // 二級緩存中沒有數據 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 底層調用queryFromDatabase tcm.putObject(cache, key, list); // 將結果放入二級緩存 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 底層調用queryFromDatabase } // BaseExecutor類,從數據庫查詢 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; }
一級緩存和二級緩存
一級緩存和二級緩存的命中判斷依據是一樣的。
一級緩存是SqlSession級別的緩存,不可關閉。同一個SqlSession對象對象執行2遍相同的SQL查詢,第二遍查詢直接返回緩存結果。
二級緩存是mapper級別的緩存。不同的SqlSession對象執行兩次相同的SQL語句,第二次查詢直接返回二級緩存中的結果。MyBatis默認是不開啟二級緩存的。
未完,待續...
Mybatis工作原理(含部分源碼)