(二十)自定義資料庫連線池
阿新 • • 發佈:2019-01-29
目錄
使用資料庫連線池優化資料庫效能
之前我們對資料庫的操作,其實是有很大問題的;
因為我們是每次操作資料庫之前,都會問資料庫要一個連線,用完之後,就把這個連結還給了資料庫;
其實資料庫連線是重量級的東西,資料庫每次建立一個連接出來,都要花老大力氣了;
因此,我們應該固定的儲存著一些連線,這些連線用完以後,就放在那裡,等下次再用,避免每次都問資料庫要 ;
連線池
程式一啟動,就問資料庫要一定數量的連線,維護在一個地方(連線池); 這樣每次,需要連線,就從池中獲取,用完之後也不返回給資料庫,而是繼續保持在池中;起到複用連線的作用; 這種變化是很驚人的;以前10000次訪問,就需要建立一千次連線。現在無論多少次訪問,都只需要建立連線池中的連線數 ;
編寫自定義資料庫連線池
編寫連線池需要實現 javax.sql.DataSource
介面。DateSource
介面中定義了 2
個過載 getConnection
的方法 :
Connection getConnection() ;
Connection getConnection(String username,String password) ;
實現
DateSource
介面,並實現連線池功能的步驟:a、在DateSource構造器中批量建立與資料庫的連線,並把建立的連線加入LinkedList物件中 ; b、實現getConnection方法,讓getConnection方法每次呼叫時,從Linkedlist中取出一個連線返回給使用者 ; c、當用戶使用完Connection以後,呼叫Connection.close()方法時,Collection物件保證將自己返回到LinkedList中,而不是把conn返給資料庫 ; 其中Collection保證將自己返回給LinkedList中是此處程式設計的難點!!
在實現 Connection getConnection()
的時候,有個問題,就是我們不能簡單的從LinkedList
裡面獲取 Connection
返回,而要是確保 linkedList
裡面將按個Connection
物件移除了,否則會造成連線衝突;
還有一個問題,就是使用者在用完 Connection
的時候,會習慣呼叫 close()
方法,這樣就導致了連線放給了資料庫,而不是返給了我們連線池;
close()
方法加強
現在就有一個問題,就是如何保證呼叫 Connection的close()
方法,是將連線還給連線池而不是資料庫 ?
這就涉及到,我們對一個類功能進行加強的問題;
在java裡面,通常有三種方法;
a、寫一個子類,覆寫父類的方法
這個做法有個致命的缺點:就是必須獲得父類的所有資訊,向我們這裡,繼承是基本不可能的!
因為,真正返回給我們的連線是,mysql的驅動類生成的連線,這個類裡面,封裝了太多的資訊:比如:連線的資料庫地址、資料庫名字、使用者名稱、連線密碼。。這些資訊 ;
我們要真的想繼承它,我們就要在子類中得到這些資訊,可惜人家都說private欄位,子類也拿不著;子類拿不著,那麼子類就連不上資料庫。。
除非你去看去mysql的驅動原始碼,自己把資訊拿出來,寫在你建立的子類裡面,但這跟重寫mysql驅動沒哈區別了,工程量嚇人!
因此,這裡放棄這種做法 ;
b、用包裝模式
寫法:
第一步:定義一個類實現與被增強類相同的介面
第二步:在類中定義一個變數,記住被增強物件
第三步:定義一個構造器,引數接受一個被增強物件的類
第四步:覆蓋我們向增強的方法 ;
第五步:對於不想增強的方法,直接呼叫被增強物件的方法 ;
這樣就完美的增強了我們想要的方法;但是也有缺點,就是介面中的每個方法,都要我們去呼叫被增強物件的方法 ;
介面中的方法少還行,一旦很多,這種程式碼,我們就要寫很多次,,,,,很噁心!
因此,應該考慮動態代理 ;
c、動態代理(aop程式設計,面向切面程式設計)
主要是利用攔截;(暫時,我也不會,以後會跟,最多20天以內更新。寫於2018年6月12日22:07:53)
程式碼:
自定義連線池程式碼。。
public class MyDataSource implements javax.sql.DataSource {
private static LinkedList<Connection> connections = new LinkedList<>();
static {
try {
// 初始化10個連接出來
for (int i = 0; i < 10; i++) {
connections.add(new MyConnection(JdbcUtils.getConnection()));
}
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
private static MyDataSource myDataSource = new MyDataSource();
public static MyDataSource getDataSource() {
return myDataSource;
}
private MyDataSource() {
}
@Override
public Connection getConnection() throws SQLException {
if (connections.size() < 1) {
throw new SQLException("資料庫連線正忙,請稍後再試...");
}
return connections.removeFirst();
}
....
....
....
// 包裝器類 ,對MyDataSource類 進行增強
static class MyConnection implements Connection {
private Connection connection;
public MyConnection(Connection connection) {
this.connection = connection;
}
// 其他方法,直接呼叫 connection的方法。。
@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
....
// 重點關注 close方法,對其進行增強,不將連線返回給資料庫
@Override
public void close() throws SQLException {
System.out.println("連線池中的剩餘連線量:"+connections.size());
connections.add(this) ;
System.out.println("成功攔截關閉!將它們返給連線池。。");
System.out.println("連線池中的剩餘連線量:"+connections.size());
}
....
// 還有許多其他方法,不一一列舉了,太多了,這也是包裝模式的缺點 。。。