首頁 > 軟體

SQL語句解析執行的過程及原理

2022-03-29 19:02:16

一、sqlSession簡單介紹

  • 拿到SqlSessionFactory物件後,會呼叫SqlSessionFactoryopenSesison方法,這個方法會建立一個Sql執行器(Executor),這個Sql執行器會代理你設定的攔截器方法。
  • 獲得上面的Sql執行器後,會建立一個SqlSession(預設使用DefaultSqlSession),這個SqlSession中也包含了Configration物件,所以通過SqlSession也能拿到全域性設定;
  • 獲得SqlSession物件後就能執行各種CRUD方法了。

二、獲得sqlSession物件原始碼分析

/**
 * 通過sqlSessionFactory.openSession進行獲取sqlSession物件
 * 原始碼位置:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
 */
public SqlSession openSession() {
	return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

/**
 * 通過資料來源去獲取SqlSession 
 * 原始碼位置:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(ExecutorType, TransactionIsolationLevel, boolean)
 */
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);
		// 根據獲取的執行器建立SqlSession
		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();
	}
}

/**
 * 獲取執行器
 * 原始碼位置:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(ExecutorType, TransactionIsolationLevel, boolean)
 */
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	// 預設使用SIMPLE的執行器
	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 {
		// 簡單的sql執行器
		executor = new SimpleExecutor(this, transaction);
	}

	// 判斷Mybatis的全域性組態檔是否開啟二級快取
	if (cacheEnabled) {
		// 開啟快取,吧executor包裝為CachingExecutor
		executor = new CachingExecutor(executor);
	}
	
	// 外掛的呼叫:責任鏈模式
	executor = (Executor) interceptorChain.pluginAll(executor);
	return executor;
}

三、SQL執行流程,以查詢為例

/**
 * 查詢的入口方法
 * 原始碼位置:org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(String, Object)
 */
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);

	// 長度為1,拿第一個
	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 {
		// 沒有拿到返回null
		return null;
	}
}

/**
 * 查詢資料
 * 原始碼位置:org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(String, Object, RowBounds)
 */
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
	try {
		// 通過statement去全域性組態檔中獲取MappedStatement(得到mapper中增刪改查的節點)
		MappedStatement ms = configuration.getMappedStatement(statement);

		// 通過執行器去執行SQL
		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();
	}
}

/**
 * 執行查詢操作的準備工作
 * 原始碼位置:org.apache.ibatis.executor.CachingExecutor.query(MappedStatement, Object, RowBounds, ResultHandler)
 */
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
		ResultHandler resultHandler) throws SQLException {

	// 通過引數進行sql解析
	BoundSql boundSql = ms.getBoundSql(parameterObject);
	CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
	return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

/**
 * 執行查詢操作的準備工作
 * 原始碼位置:org.apache.ibatis.executor.CachingExecutor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
 */
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
		ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
	// 判斷sql是否開啟了快取 <cache></cache>
	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);
}

/**
 * 一級快取查詢的呼叫
 * 原始碼位置:org.apache.ibatis.executor.BaseExecutor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
 */
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;
}

/**
 * 在資料庫中查詢
 * 原始碼位置:org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
 */
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;
}

/**
 * 查詢邏輯
 * 原始碼位置:org.apache.ibatis.executor.SimpleExecutor.doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql)
 */
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();

		// 內部封裝了ParameterHandler和ResultSetHandler
		StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,
				resultHandler, boundSql);
		stmt = prepareStatement(handler, ms.getStatementLog());

		// 執行查詢
		return handler.<E>query(stmt, resultHandler);
	} finally {
		closeStatement(stmt);
	}
}

/**
 * 執行查詢語句
 * 原始碼位置:org.apache.ibatis.executor.statement.SimpleStatementHandler.query(Statement, ResultHandler)
 */
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
	// 得到要執行的sql
	String sql = boundSql.getSql();

	// 執行sql
	statement.execute(sql);

	// 處理結果集
	return resultSetHandler.<E>handleResultSets(statement);
}

到此這篇關於SQL語句解析執行的過程及原理的文章就介紹到這了,更多相關SQL語句解析原理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com