摘要:問題記錄工作環境是使用使用用的,在一次調試中。發現每一次插入一條數據都會創建一個。如圖圖問題可能的原因原因分析沒有使用緩存因為這個是插入,不是查詢,所以這里不存在什么緩存的問題。但是在在關閉的時候也是做了判斷。
問題記錄:
工作環境是使用spring boot,使用用的mybatis,在一次調試中。發現每一次插入一條 數據都會創建一個SqlSession。如圖:
圖1:
問題可能的原因: 原因分析:#1 沒有使用緩存因為這個是插入,不是查詢,所以這里不存在什么緩存的問題。
后來百度了一波,網上說是沒有使用事務。加上@Transactional
圖2:
發現“Creating a new SqlSession”這兩個煩人的東西居然還在。不管了,直接分析源碼
直接分析源碼,老子還不信了,搞不定你我還混什么: 1.開啟debug 2.打上斷點
圖3:
發現session為空
繼續走... 圖2 #分析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; }圖3 #分析
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} 補充
sqlSessionHolder 繼承了spring的ResourceHolderSupport
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。如果回話不存在事務,則直接關閉session
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 holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); 這一句:他從從前線程的threadLocal 中獲取sqlSessionHolder。但是在在sqlSession 關閉session 的時候,sqlSessionHolder也是做了判斷。如果會話在事務中,就減少引用次數,沒有真實關閉session。如果會話不存在事務,則直接關閉session。也就是說,必須開啟事務,但這個問題好像只是插入了一下,事務已經執行完成了,下一次插入的時候,由于上一個事務執行完成了, 如果不存在holder或沒有被事務鎖定,則會創建新的sqlSession,即 Creating a new SqlSession,通過sessionFactory.openSession()方法。如果會話不存在事務,就直接把session關閉了,同時,也減少了引用次數。
換一句話來說:如果在插入的代碼塊中,再加上一個查詢的代碼,或者再插入一條數據的代碼,這樣就不會出現Creating a new SqlSession這個煩人的家伙。好了,祝大家好運!!!
引用:SqlSessionHolder作用
如果有侵權,馬上刪除
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69739.html
摘要:相關閱讀通過項目逐步深入了解一通過項目逐步深入了解二通過項目逐步深入了解三本項目所有代碼及文檔都托管在地址延遲加載什么是延遲加載可以實現高級映射使用實現一對一及一對多映射,具備延遲加載功能。一級緩存是級別的緩存。 相關閱讀: 1、通過項目逐步深入了解Mybatis 2、通過項目逐步深入了解Mybatis 3、通過項目逐步深入了解Mybatis 本項目所有代碼及文檔都托管在 Github...
摘要:什么是本是的一個開源項目年這個項目由遷移到了,并且改名為。如下的代碼,如果有多個條件的話,那么拼接起來很容易出錯查詢語句根據是否為來判斷是否是條件查詢。而如果我們使用的話,就可以免去查詢助手類了。 什么是MyBatis MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,...
摘要:得到用戶信息,將用戶信息存儲到一級緩存中。如果中間去執行操作執行插入更新刪除,則會清空中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。 基礎: 1、 概念:Java當中的一個持久層框架。2、 特點、優勢:(1)把java代碼和SQL代碼做了一個完全分離。(2)良好支持復雜對象的映射(輸入映射、輸出映射)(3)使用動態SQL,可以預防SQL注入。3、 ...
摘要:將語句硬編碼到代碼中,修改語句需要重新編譯代碼設想使用配置文件配置。從結果集中遍歷數據的時候存在硬編碼。表示一個拼接符號,會引用注入,所以不建議使用。和表示查詢出一條記錄進行映射。 MyBatis是什么 mybatis是托管在github上的ORM框架,讓程序員將主要精力放在SQL上,通過mybatis提供映射方式,自由靈活(SQL的可定制性較高,半自動化)生成滿足需求的SQL語句。m...
閱讀 2178·2021-11-24 09:38
閱讀 3242·2021-11-08 13:27
閱讀 3083·2021-09-10 10:51
閱讀 3143·2019-08-29 12:20
閱讀 663·2019-08-28 18:28
閱讀 3459·2019-08-26 11:53
閱讀 2706·2019-08-26 11:46
閱讀 1515·2019-08-26 10:56