工做環境是使用spring boot,使用用的mybatis,在一次調試中。發現每一次插入一條 數據都會建立一個SqlSession。如圖:
圖1:html
由於這個是插入,不是查詢,因此這裏不存在什麼緩存的問題。
後來百度了一波,網上說是沒有使用事務。
圖2:spring
發現「Creating a new SqlSession」這兩個煩人的東西竟然還在。
直接分析源碼,老子還不信了,搞不定你我還混什麼: 1.開啓debug 2.打上斷點
圖3:sql
發現session爲空緩存
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { //從從前線程的threadLocal 中獲取sqlSessionHolder SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); //調用靜態方法sessionHoler 判斷是否存在符合要求的sqlSession SqlSession session = sessionHolder(executorType, holder); // 判斷當前sqlSessionHolder 中是否持有sqlSession (即當前操做是否在事務當中) if (session != null) { //若是持有sqlSesison 的引用,則直接獲取 return session; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } //獲取新的sqlSession 對象。這裏由sessionFacory產生的defaultSqlSession session = sessionFactory.openSession(executorType); //判斷判斷,當前是否存在事務,將sqlSession 綁定到sqlSessionHolder 中,並放到threadLoacl 當中 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; if (holder != null && holder.isSynchronizedWithTransaction()) { //hodler保存的執行類型和獲取SqlSession的執行類型不一致,就會拋出異常,也就是說在同一個事務中,執行類型不能變化,緣由就是同一個事務中同一個sqlSessionFactory建立的sqlSession會被重用 if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction"); } //增長該holder,也就是同一事務中同一個sqlSessionFactory建立的惟一sqlSession,其引用數增長,被使用的次數增長 holder.requested(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); } //返回sqlSession session = holder.getSqlSession(); } return session; }
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; //判斷事務是否存在 if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); //加載環境變量,判斷註冊的事務管理器是不是SpringManagedTransaction,也就是Spring管理事務 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]"); } holder = new SqlSessionHolder(session, executorType, exceptionTranslator); //若是當前回話處在事務當中,則將holder 綁定到ThreadLocal 中 //以sessionFactory爲key,hodler爲value,加入到TransactionSynchronizationManager管理的本地緩存ThreadLocal<Map<Object, Object>> resources中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); //將holder, sessionFactory的同步加入本地線程緩存中ThreadLocal<Set<TransactionSynchronization>> synchronizations TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); //設置當前holder和當前事務同步 holder.setSynchronizedWithTransaction(true); //holder 引用次數+1 holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional"); } } else { throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); } } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"); } }
sqlSessionHolder 繼承了spring的ResourceHolderSupportsession
public abstract class ResourceHolderSupport implements ResourceHolder { //事務是否開啓private boolean synchronizedWithTransaction = false; private boolean rollbackOnly = false; private Date deadline; // 引用次數 private int referenceCount = 0; private boolean isVoid = false; }
在sqlSession 關閉session 的時候, 使用了工具了sqlSessionUtils的closeSqlSession 方法。sqlSessionHolder 也是作了判斷,若是回話在事務當中,則減小引用次數,沒有真實關閉session。若是回話不存在事務,則直接關閉sessionmybatis
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); //若是holder 中持有sqlSession 的引用,(即會話存在事務) if ((holder != null) && (holder.getSqlSession() == session)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Releasing transactional SqlSession [" + session + "]"); } //每當一個sqlSession 執行完畢,則減小holder 持有引用的次數 holder.released(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Closing non transactional SqlSession [" + session + "]"); } //若是回話中,不存在事務,則直接關閉session session.close(); } }
到了這一步,問題已經很明顯了。工具
SqlSessionHolder做用
若是有侵權,立刻刪除spa