Creating a new SqlSession
SqlSession [[email protected]] was not registered for synchronization because synchronization is not active
JDBC Connection [[email protected]] will not be managed by Spring
==> Preparing: SELECT app_id, app_code, app_name FROM t_sys_application
==> Parameters:
<== Columns: app_id, app_code, app_name
<== Total: 2
Closing non transactional SqlSession [[email protected]]
在springboot配置了mybatis、Druid資料庫連接配接池後,發現每次sql執行mybatis都:
Creating a new SqlSession
Closing non transactional SqlSession
以為資料庫連接配接池沒有生效,就去看了一下。直接上結論:mybatis的sqlSession和資料庫連接配接池中維護的資料庫連接配接Collection不是同一個概念,SqlSession是mybatis架構中的概念,是mybatis持久層架構的頂層API。在sqlSession中操作資料庫的時候會去擷取collection,collection的擷取是去連接配接池中取的!是以Creating a new SqlSession并不是每次都去建立了資料庫新連接配接,底層使用的collection還是連接配接池提供的。至于每次sql執行,mybatis都Creating a new SqlSession而不是共享SqlSession,是為了保證sql會話獨立避免發生髒資料,進而保證sql線程安全。
源碼随筆:
org.mybatis.spring.SqlSessionUtils.getSqlSession():
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
//擷取SqlSession
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
sessionFactory.openSession(executorType)的實作:DefaultSqlSessionFactory.openSession()
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//通過Confuguration對象去擷取Mybatis相關配置資訊, Environment對象包含了資料源和事務的配置
// execType為執行器類型,配置檔案中定義
// SimpleExecutor -- SIMPLE 就是普通的執行器。
//ReuseExecutor -執行器會重用預處理語句(prepared statements)
//BatchExecutor --它是批量執行器
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//定義執行器,是對statement的封裝
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();
}
}
得到SqlSession對象之後就可以利用SqlSession提供的方法進行CRUD操作了。Connection對象是在SqlSession對象建立之後進行CURD操作中建立的。深入查找之後找到在ManagedTransaction類中找到擷取Connection對象的關鍵代碼如下:
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//dataSource 來源有三種,JndiDatasource,PooledDataSource,UnpooledDataSource,配置檔案中定義
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
PooledDataSource和UnPooledDataSource的差別是PooledDataSource使用了連接配接池。為什麼使用連接配接池呢?因為建立一個Connection對象的過程,在底層就相當于和資料庫建立的通信連接配接,在建立通信連接配接的過程,消耗了非常多的時間,而往往我們建立連接配接後(即建立Connection對象後),就執行一個簡單的SQL語句,然後就要抛棄掉,這是一個非常大的資源浪費!mybatis針對這一個問題提出的PooledDataSource使用了連接配接池。
補充:
在MyBatis中,多個SqlSession可以複用同一個Connection,同一個SqlSession中可以送出多個事務。
在service方法上添加@Transactional 開啟事務,把資料庫事務委托給spring管理,這樣多個sql執行就可以共用同一個SqlSession。