Mybatis中的數據源與連接池
在使用Mybatis框架時需要對核心配置文件進行設置,其中就包括了數據源和連接池。
本文只進行原理的描述,不包含實際代碼。
一、爲什麼要使用連接池?
創建一個Conncection代價是巨大的,因爲創建了一個連接,在底層就與數據庫建立了通信連接,
連接之後往往只進行簡單的sql操作,浪費了巨大的資源。
二、數據庫連接池的基本思想
爲數據庫建立一個“連接池”,預先創建一定數量的連接,當需要連接時,只需從中取出一個,使用
完畢之後再放回。
三、數據源與連接池的描述
1.數據源的分類
(1)UNPOOLED 不使用連接池的數據源
--用實現了java.sql.DataSource的UnpooledDataSource類來表示
(2)POOLED 使用連接池的數據源
--用實現了java.sql.DataSource的PooledDataSource類來表示
(3)JNDI 使用JNDI實現的數據源
--通過JNDI上下文中取值
註釋:PooledDataSource和UnpooledDataSource都實現了java.sql.DataSource接口。
可是當PooledDataSource需要創建java.sql.Connection對象時,是通過UnpooledDataSource來創建的,因爲在PooledDataSource中持有一個UnpooledDataSource的引用,PooledDataSource只是提供一種緩存連接池機制。
2.數據源DataSource的創建過程
Mybatis數據源DataSource對象的創建發生在MyBatis初始化過程中。
(1)Mybatis初始化時,解析xml,根據<datasource>中的type屬性來創建相應類型的DataSource。
註釋:Mybatis創建DataSource根據工廠模式,三種工廠分別是
UnpooledDataSourceFactory 實現了DataSourceFactory接口
JndiDataSourceFactory 實現了DataSourceFactory接口
PooledDataSourceFactory 繼承了UnpooledDataSourceFactory
(2)創建DataSource實例之後,會將實例放入Configuration對象中的Environment對象中。
3.DataSource什麼時候創建Connection對象
創建SqlSession對象執行SQL語句時才調用相應的DataSource來創建Connection對象。
例如執行到sqlSession.selectList("SELECT * FROM STUDENTS"); 才創建Connection對象。
4.不使用連接池的UnpooledDataSource
使用UnpooledDataSource的getConnection(),每調用一次就會產生一個新的Connection實例對象。
5.使用了連接池的PooledDataSource
PooledDataSource將Connection對象包裹成PooledConnection對象放入PoolState類型容器中。
Mybatis將PoolConnection分爲兩種狀態:空閒狀態(idle)和活動狀態(active),這兩種狀態
被分別存儲到PoolState容器內的idleConnections和activeConnections兩個List集合中。
idleConnections:idle狀態的PooledConnection放入其中,表示當前沒被使用的連接,需要連接時,優先從中取出。當用完一個Connection對象後,又會被包裹成PooledConnection放到此集合中。
activeConections:active狀態的PoolConnection放入其中,表示正在使用。當需要連接時,優先從idleConnections中取,如果沒有,就看activeConnections集合是否已經滿了,沒滿的話PooledDataSource會創建出一個PooledConnection添加到這個集合中,並返回。
6.獲取java.sql.Connection對象的過程
先看是否有空閒(idle)狀態下的PooledConnection對象,如果有,就直接返回一個可用的PooledConnection對象;否則進行第2步。
查看活動狀態的PooledConnection池activeConnections是否已滿;如果沒有滿,則創建一個新的PooledConnection對象,然後放到activeConnections池中,然後返回此PooledConnection對象;否則進行第3步;
看最先進入activeConnections池中的PooledConnection對象是否已經過期:如果已經過期,從activeConnections池中移除此對象,然後創建一個新的PooledConnection對象,添加到activeConnections中,然後將此對象返回;否則進行第4步。
線程等待,循環2步(代碼見參考資料)
7.java.sql.Connection對象的回收
如何將用完之後的連接自動放回連接池,而不是手動將Connection放到Pool連接池?
所以應該實現Connection對象調用了close()方法,實際上卻是將對象添加到連接池。
這是要使用代理模式,爲真正的Connection對象創建一個代理對象,
代理對象所有的方法都是調用相應的真正Connection對象的方法實現。當代理對象執行close()方法時,
要特殊處理,不調用真正Connection對象的close()方法,而是將Connection對象添加到連接池中。
具體代碼見參考資料。
8.JNDI類型的數據源DataSource
對於JNDI類型的數據源DataSource的獲取就比較簡單,MyBatis定義了一個JndiDataSourceFactory工廠
來創建通過JNDI形式生成的DataSource。具體代碼見參考資料。