1. 程式人生 > >(二十)自定義資料庫連線池

(二十)自定義資料庫連線池

目錄

使用資料庫連線池優化資料庫效能

之前我們對資料庫的操作,其實是有很大問題的;

因為我們是每次操作資料庫之前,都會問資料庫要一個連線,用完之後,就把這個連結還給了資料庫;

其實資料庫連線是重量級的東西,資料庫每次建立一個連接出來,都要花老大力氣了;

因此,我們應該固定的儲存著一些連線,這些連線用完以後,就放在那裡,等下次再用,避免每次都問資料庫要 ;

連線池

程式一啟動,就問資料庫要一定數量的連線,維護在一個地方(連線池);

這樣每次,需要連線,就從池中獲取,用完之後也不返回給資料庫,而是繼續保持在池中;起到複用連線的作用;

這種變化是很驚人的;以前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());
        }


    ....

    // 還有許多其他方法,不一一列舉了,太多了,這也是包裝模式的缺點 。。。