為什麼mybatis中的SqlSession一定要關閉
mybatis的SqlSession一定要關閉
今天在使用mybatis查詢數據時,出現瞭一個很奇怪的問題。同一條sql語句,查詢時快時慢,並且有一定的規律性,大概每10次查詢中有一次會特別特別的慢,快的隻需要1ms,慢的要20000ms,sql代碼及快慢時間截圖如下:
select fknr from jq_fkqk where jjxh = ?
快的情況
慢的情況
通過日志打印mybatis查詢信息時,我觀察到特別慢的時候,並不是因為它查詢很慢,而是因為它需要等待一段很長的時間才開始:
==> Preparing: select fknr from jq_fkqk where jjxh = ?
說明時間長就出現在這裡,這個等待的時間。
為什麼會要等待這麼久呢?我自然而然的看瞭一下上一次查詢的函數,發現在函數裡面沒有session.close(),把這一句加上,問題就解決瞭,速度就飛快瞭。
我本來想查看官方文檔,查看原因,但是沒找到,隻能結合我自己的理解來分析一下。SqlSession是通過SqlSessionFactory來構造的,相當於維護一個連接池,當我們不停的進行查詢的時候,由於沒有關閉連接,導致與數據庫的連接數量達到瞭一個上限(可能連接池有最大連接數,但是我們有找到文檔)。
到達上限之後,再次請求查詢時,Factory說沒有連接瞭,讓你先等一下,它先去判斷哪些SqlSession已經沒有人使用瞭(類似於垃圾回收機制),然後調用相應的進程去自動關閉沒用的session連接,註意調用進程可是要排隊的,也要耗時間。
等關閉瞭沒有用的session之後,Factory通知你,有空閑的session瞭,開始準備你的查詢吧,所有才會有等待很長一段時間才出現:
==> Preparing: select fknr from jq_fkqk where jjxh = ?
這純屬我自己的理解,但是重點還是表達出來瞭,就是Mybatis中的session一定要手動去關閉它,session.close(),不然會占著資源,導致性能下降!!!
mybatis中SqlSession使用事項
一.SqlSession的使用范圍
SqlSession中存儲的是編譯好的sql語句,這些sql語句是mybatis配置文件讀取mapper.xml文件生成的,將sql語句存儲到SqlSessionFactory和SqlSession中。封裝瞭對數據庫的操作,如:查詢、插入、更新、刪除等。
通過SqlSessionFactory創建SqlSession,而SqlSessionFactory是通過SqlSessionFactoryBuilder加載配置文件進行創建。在開發中SqlSession在每次使用完都要進行關閉,使用時創建,也就是多例的,線程安全。SqlSessionFactory在整個類中隻有一個對象,也就是單例的,單例的線程不安全。
二.SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用於創建SqlSessionFacoty,SqlSessionFacoty一旦創建完成就不需要SqlSessionFactoryBuilder瞭,因為SqlSession是通過SqlSessionFactory生產,所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用范圍是方法范圍即方法體內局部變量。
三.SqlSessionFactory
SqlSessionFactory是一個接口,接口中定義瞭openSession的不同重載方法,SqlSessionFactory的最佳使用范圍是整個應用運行期間,一旦創建後可以重復使用,通常以單例模式管理SqlSessionFactory。
四.SqlSession
SqlSession是一個面向用戶的接口, sqlSession中定義瞭數據庫操作,默認使用DefaultSqlSession實現類。
執行過程如下:
1、加載數據源等配置信息
Environment environment = configuration.getEnvironment();
2、創建數據庫鏈接
3、創建事務對象
4、創建Executor,SqlSession所有操作都是通過Executor完成,mybatis源碼如下:
if (ExecutorType.BATCH == executorType) { executor = newBatchExecutor(this, transaction); } elseif (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); }
SqlSession的實現類即DefaultSqlSession,此對象中對操作數據庫實質上用的是Executor
小結一下
每個線程都應該有它自己的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。因此最佳的范圍是請求或方法范圍。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
打開一個 SqlSession;使用完畢就要關閉它。通常把這個關閉操作放到 finally 塊中以確保每次都能執行關閉。如下:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 詳解MyBatis工作原理
- 一篇文章帶你學習JAVA MyBatis底層原理
- Mybatis執行流程、緩存原理及相關面試題匯總
- 初次體驗MyBatis的註意事項
- 關於MyBatis中SqlSessionFactory和SqlSession簡解