<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在資料庫應用中,使用者端與資料庫伺服器端建立的連線物件(Connection)是寶貴的資源,每次請求資料庫都建立連線,使用完畢後會銷燬連線,這是一種很浪費資源的操作。因此Java提出了DataSource介面。可以把它當作一個連線池。程式初始化時,建立一批連線放入到連線池中,如果需要請求資料庫就從連線池中取出連線物件(Connection)使用完畢後把連線歸還給連線池。這樣就減少了每次請求都建立、銷燬連線的步驟,從而提高資料庫效能。
package javax.sql; public interface DataSource extends CommonDataSource, Wrapper { // 最重要的方法 Connection getConnection() throws SQLException; // 其他方法不再列出 }
Java只是在JDK1.4版本釋出了該介面規範。具體實現需要使用者自己實現。MyBatis中提供了3種DataSource介面的實現。
下面著重分析1和2這兩種DataSource的實現。
UnpooledDataSource顧名思義,他是非池化的DataSource,說白了和普通的Connection沒什麼區別。通過UnpooledDataSource過去連線每次都需要重新建立一個Connection。我們來看下它的getConnection實現方法。
public Connection getConnection() throws SQLException { return doGetConnection(username, password); } private Connection doGetConnection(Properties properties) throws SQLException { initializeDriver(); Connection connection = DriverManager.getConnection(url, properties); configureConnection(connection); return connection; }
在UnpooledDataSource#getConnection方法中,呼叫了doGetConnection方法,引數是username和password,該方法也就是通過使用者名稱和密碼獲取資料庫連線的意思。doGetConnection具體實現就使用了DriverManager來獲取連線物件。這是JDBC原生獲取連線物件的方式。
值得一說的是:UnpooledDataSource的其他方法都是基於DriverManager實現的。也就是說,使用UnpooledDataSource作為連線池的話等價於沒有使用連線池。
PooledDataSource才是真正意義上的連線池,它提供了連線池的大小(預設10)、最大活躍連線數量、空閒連線數量等蠶食設定。並且對Connection物件進行了JDK動態代理,重寫了Connection的close
方法。使得Connection物件在呼叫close方法是不是真正的關閉連線,而是把自定義關閉行為,MyBatis的關閉邏輯就是把Connection物件歸還連線池。
我們先看下PooledDataSource的幾個重要欄位資訊
public class PooledDataSource implements DataSource { // PooledDataSource真正管理連線狀態的是PoolState,後面會詳細說明 private final PoolState state = new PoolState(this); // UnpooledDataSource上面說過和普通的Connection無異 private final UnpooledDataSource dataSource; //正在使用連線的數量 protected int poolMaximumActiveConnections = 10; //空閒連線數 protected int poolMaximumIdleConnections = 5; //在被強制返回之前,池中連線被檢查的時間 protected int poolMaximumCheckoutTime = 20000; //這是給連線池一個列印紀錄檔狀態機會的低層次設定,還有重新 嘗試獲得連線, 這些情況下往往需要很長時間 為了避免連線池沒有設定時靜默失 敗)。 protected int poolTimeToWait = 20000; //傳送到資料的偵測查詢,用來驗證連線是否正常工作,並且準備 接受請求。預設是「NO PING QUERY SET」 ,這會引起許多資料庫驅動連線由一 個錯誤資訊而導致失敗 protected String poolPingQuery = "NO PING QUERY SET"; //開啟或禁用偵測查詢 protected boolean poolPingEnabled = false; //用來設定 poolPingQuery 多次時間被用一次 protected int poolPingConnectionsNotUsedFor = 0; private int expectedConnectionTypeCode; }
這些欄位主要記錄了連線池的重要資訊:連線池大小、空閒時最大連線數、最大活躍連線數、超時時間等。而整整揭開PooledDataSource獲取連線物件的神祕面紗還需要介紹兩個類。PooledConnection和PoolState
PooledConnection實現了InvocationHandler介面,他是用來做JDK動態代理的。前文提到過,mybatis使用JDK動態代理重寫了Connection物件的close方法,就是在該類中實現的邏輯。該類有幾個重要屬性。
接下來來看下代理物件的invoke方法是如何重寫close方法的。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); //如果呼叫close的話,忽略它,反而將這個connection加入到池中 if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } return method.invoke(realConnection, args); // 其他邏輯省略.... }
在invoke方法中判斷下執行的方法名稱是否是Close,如果是,就不再執行原來的close方法了,而是執行PooledDataSource 的pushConnection方法!從方法名可以看出方法的作用是:把連線push到連線池PooledDataSource 中。pushConnection的邏輯後文詳細說明
上文提到PooledDataSource並不管理連線物件。那麼程式初始化的時候建立的一批連線存放到哪裡了呢?答案是存在PoolState物件中,而PooledDataSource有一個屬性就是PoolState。也就是說PooledDataSource是通過PoolState來管理連線池的。
一批連線在Java中就是一個List集合嘛。那麼我們想一下PoolState都需要怎麼管理連線呢?首先根據連線的狀態,可以把連線分為2種
protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
PoolState中兩個List屬性分別儲存空閒連線和活躍連線。需要連線的時候就從idleConnections
列表中取,關聯連線時就把連線從activeConnections
中移到idleConnections
中。
PoolState中還有一些其他的統計資訊欄位,比如 請求次數、請求的總時間、總連線數等這些屬性比較簡單就不再列出了
介紹完PooledConnection和PoolState這兩個類後,我們來看下PooledDataSource是怎麼獲取連線的。獲取連線的邏輯在PooledDataSource#getConnection方法中,getConnection方法只是一個殼子,具體呼叫邏輯在popConnection方法。我們來看一下(我只列出了重要邏輯)
public Connection getConnection() throws SQLException { return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection(); } private PooledConnection popConnection(String username, String password) throws SQLException { //最外面是while死迴圈,如果一直拿不到connection,則不斷嘗試 while (conn == null) { synchronized (state) { if (!state.idleConnections.isEmpty()) { //如果有空閒的連線的話,返回第一個空閒連線 conn = state.idleConnections.remove(0); } else { //如果沒有空閒的連線 if (state.activeConnections.size() < poolMaximumActiveConnections) { //如果activeConnections太少,那就new一個PooledConnection conn = new PooledConnection(dataSource.getConnection(), this); } else { //如果activeConnections已經很多了,那不能再new了 //取得activeConnections列表的第一個(最老的) PooledConnection oldestActiveConnection = state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > poolMaximumCheckoutTime) { //如果checkout時間過長,則這個connection標記為overdue(過期) //刪掉最老的連線,然後再new一個新連線 conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this); oldestActiveConnection.invalidate(); } else { //如果checkout時間不夠長,沒辦法,只能等待,在此分支會記錄一些統計資訊 } } } if (conn != null) { if (conn.isValid()) { //如果已經拿到connection,則記錄一些統計資訊 } else { //如果沒拿到,統計資訊:壞連線+1 state.badConnectionCount++; localBadConnectionCount++; conn = null; //如果好幾次都拿不到,就放棄了,丟擲異常 } } } } return conn; }
在popConnection中
在PooledConnection小節中見到,PooledConnection重寫了Connection的close方法。當呼叫Connection的close方法時真正執行的邏輯是PooledDataSource的pushConnection方法。該程式碼邏輯很簡單,大體上說,就是把連線從活躍列表中刪除,加入到空閒列表中。具體實現如下
protected void pushConnection(PooledConnection conn) throws SQLException { synchronized (state) { //先從activeConnections中刪除此connection state.activeConnections.remove(conn); if (conn.isValid()) { if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) { //如果空閒的連線太少, state.accumulatedCheckoutTime += conn.getCheckoutTime(); if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } //new一個新的Connection,加入到idle列表 PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this); state.idleConnections.add(newConn); //通知其他執行緒可以來搶connection了 state.notifyAll(); } else { //否則,即空閒的連線已經足夠了 state.accumulatedCheckoutTime += conn.getCheckoutTime(); //那就將connection關閉就可以了,獲取真正的connection物件並且關閉 conn.getRealConnection().close(); conn.invalidate(); } } } }
關閉過程:
獲取MyBatis提供的DataSource實現,需要通過工廠DataSourceFactory
介面來獲取。在這裡MyBatis使用了工廠方法模式。DataSourceFactory有兩個實現類。分別是
我們首先來看下工廠介面定義
public interface DataSourceFactory { //設定屬性,被XMLConfigBuilder所呼叫 void setProperties(Properties props); //生產資料來源,直接得到javax.sql.DataSource DataSource getDataSource(); }
其中最重要的方法就是getDataSource,它很直觀,通過工廠物件的該方法可以獲取DataSource實現。
UnpooledDataSourceFactory獲取dataSource的方法非常簡單直觀。
首先,構造方法裡裡new了一個UnpooledDataSource物件存放到工廠的屬性中
然後,getDataSource直接返回該物件即可。具體實現如下
public class UnpooledDataSourceFactory implements DataSourceFactory { protected DataSource dataSource; public UnpooledDataSourceFactory() { this.dataSource = new UnpooledDataSource(); } public DataSource getDataSource() { return dataSource; } }
PooledDataSourceFactory就有意思了,想偷懶,直接繼承自UnpooledDataSourceFactory。只需要在構造方法中new一個PooledDataSource物件,再通過getDataSource方法獲取即可。
public class PooledDataSourceFactory extends UnpooledDataSourceFactory { //資料來源換成了PooledDataSource public PooledDataSourceFactory() { this.dataSource = new PooledDataSource(); } }
個人感覺mybatis提供的DataSourceFactory的實現類有點雞肋。可以說還是new物件。我們知道工廠模式建立的一般都是比較複雜的物件,是用來幫助開發者遮蔽複雜的細節。而mybatis的這兩個實現都只是new物件而已。
以上就是MyBatis基礎支援DataSource實現原始碼解析的詳細內容,更多關於MyBatis基礎支援DataSource的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45