標籤:his 重點 tsql oss 線程 [] bsp .sql dea
在之前的文章中我們瞭解到最終的資料庫最終操作是走的代理類的方法:
@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 #22 closeSqlSession(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的執行緒安全性