spring如何管理mybatis(二) ----- SqlSession的線程安全性
阿新 • • 發佈:2018-05-08
ger getc 初始 安全性 tco 當我 ade loaded 當前
在之前的文章中我們了解到最終的數據庫最終操作是走的代理類的方法:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close()sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } }
我們可以看到每次都是使用getSqlSession()來獲取真是sqlsession的,而獲取的sqlSession又是DefaultSqlSession,這個類我們知道他是線程不安全的,之前使用都是采用多實例模式,就是每次使用都new一個,但是spring采用了更加聰明的方式可以使它不需要每次new一個也可以保持線程安全。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
我們看到
TransactionSynchronizationManager.getResource(sessionFactory)
這個方法,他的作用主要是在當前線程的事務管理中獲取一個session的持有者。
sessionHolder(executorType, holder)
這個方法,他的作用是獲取一個session資源,並進行登記。
如果沒有獲取到session,就會自己創建一個並執行
registerSessionHolder
這個方法,將創建的session試圖放進當前的線程上下文中。
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { holder = new SqlSessionHolder(session, executorType, exceptionTranslator); TransactionSynchronizationManager.bindResource(sessionFactory, holder); TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); holder.requested(); } }
我們重點看看這個方法,首先
TransactionSynchronizationManager.isSynchronizationActive()
public static boolean isSynchronizationActive() { return (synchronizations.get() != null); }
這個方法主要判斷當前線程的synchronizations是不是有值的,那這個值得初始化在哪裏呢,他的初始化發生在事務攔截器中,當創建一個事務時會為當前的線程添加synchronizations,當該事務結束時會將他清空。
holder = new SqlSessionHolder(session, executorType, exceptionTranslator); TransactionSynchronizationManager.bindResource(sessionFactory, holder); TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); holder.requested();
接下來的就是創建一個session的持有者,然後把他綁定到當前的事務管理中,這樣只要是在該事務中的事務操作都可以使用這個sqlsession,因為他們一定是在同一線程,他們的動作一定是互斥的,這樣可以保證線程的安全性。
綜上所述,我們可以總結下,當我們的程序被spring的事務管理時,程序中的數據庫操作可以使用同一個SqlSession,而不會產生線程安全問題。
spring如何管理mybatis(二) ----- SqlSession的線程安全性