DataSource超高級應用——對DataSource的合理封裝

  
轉自:cnjbb論壇的jdbc專欄的axman大俠寫的-DataSource超高級應用
鏈接:
http://www.cnjbb.org/thread.jsp?boardid=23&threadid=34286


    在前面的介紹中,我們可以看出, DataSource才能提供最高性能的對數據庫的併發訪問,
但是,對DataSource的引用,也還有很多知識要弄清楚,獲取Connection的方式是數據庫性能最相
關的技術,而對DataSource的調用對數據庫性能起着很大的決定作用。
    一般對於DataSource的引用是通過以下流程來進行:
            Context ct = new InitialContext();
            DataSource ds = (DataSource) ct.lookup(sourceUrl);
    就這麼簡單的兩行,但其調用條件不同卻可以產生性能上巨大的差別。因爲一個取得
Connect的封裝類(Bean)要對DataSource的引用,會有着多種方式。如果是作爲一個普通的封
裝類,我們可以在構造方法中調用,而作爲javaBean可以在init方法中調用,但無論要哪兒調用,
當我們對這個類或Bean進行調用時,都要對DataSource進行查找。其實DataSource的查找過程也
是一個相當消耗資源的過程,所以我們應該把DataSource聲明爲靜態資源,因爲即使你對封裝類
或Bean中每次調用都進行一次查找,事實上你得到的DataSource還是那個不變的唯一的資源。那
麼爲什麼不把它聲明爲static呢?這樣只在需要的時候纔去查找,而更多的時候就可以直接對已
經查找到的DataSource進行直接引用:

public class ConnectionFactory{
    private static DataSource dsCache = null;

    aMethodForGetConnectio(){
        if(dsCache == null){
            synchronized(this){
                if(dsCache==null){
                    Context ct = new InitialContext();
                    dsCache = (DataSource) ct.lookup(source);
                }
            }
        }
        Connection conn = dsCache.getConnection();
    }
}
我們看到,一般情況下,只有當第一次調用ConnectionFactory時纔會對DataSource進行查找,而
其它時候只要不發生意外(目前我還想不出有什麼情況會使原來不是null的DataSource突然null)
就不再需要查找而直接引用了.而如果你不把DataSource聲明爲靜態的,那麼每次對調用
ConnectionFactory都要產生一次jndi的查找:
public class ConnectionFactory{
    private DataSource dsCache = null;

    aMethodForGetConnectio(){
        if(dsCache == null){
            Context ct = new InitialContext();
            dsCache = (DataSource) ct.lookup(source);
        }
        Connection conn = dsCache.getConnection();
    }
}
因爲dsCache不是靜態資源,所以每次調用,if(dsCache==null)都成立.
有人說那Connection如果也是靜態的那不也節省資源了嗎?注意Connection不是工場,如果多個用
戶同時對同一Connection訪問,那麼任何人都不能關閉Connection,而最後變成誰也沒有關閉
Connection,因爲最後一次使用的用戶根本不知道後面到底是否有人要使用.另一方面,一個
Connection處理能力很低,不可能同時滿足很多用戶同時對數據庫存取數據.而DataSource雖然是
多個用戶對同一資源的引用,但它是工廠,不同用戶訪問同一DataSource得到的是不同的Connection
資源.

爲了對特定DataSource進行查找,我們要對jndi的引用進行配置,一般來說,如果一個封裝類或Bean
不可能將jndi引用字符串寫死要代碼中,可以放在配置文件中然後讀取:
    Properties properties = new Properties();
    properties.load(new FileInputStream("配置文件"));
    dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
這樣在我們要修改DataSource時就可以難過修改"配置文件"而不要重新編譯ConnectionFactory,但
是如果是對一個容器下的多個應用同時配置了多個DataSource,我們根本無法指定到底查打哪個數據
源,所以最好能重載一個方法,無論是構造方法還是其它方法都應該重載一個根據參數查找的方法:
public class ConnectionFactory{
    private static DataSource dsCache = null;

    aMethodForGetConnectio(){
        if(dsCache == null){
            synchronized(this){
                if(dsCache==null){
                    Properties properties = new Properties();
                    properties.load(new FileInputStream("配置文件"));
                    Context ct = new InitialContext();
                    dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                }
            }
        }
        Connection conn = dsCache.getConnection();
    }
    aMethodForGetConnectio(String source){
        if(dsCache == null){
            synchronized(this){
                if(dsCache==null){
                    Context ct = new InitialContext();
                    dsCache = (DataSource) ct.lookup(source);
                }
            }
        }
        Connection conn = dsCache.getConnection();
    }

}

另外對於默認的查找字符串,我們仍然可以做進一步的優化,不過即使每次讀取屬性,性能也沒有太大的影響:
public class ConnectionFactory{
    private static DataSource dsCache = null;
    private static Properties properties = new Properties();
    aMethodForGetConnectio(){
        if(dsCache == null){
            synchronized(this){
                if(dsCache==null){
                    if(!properties.containsKey("key"))
                        properties.load(new FileInputStream("配置文件"));
                    //只有在沒有找到屬性時纔去再讀配置文件
                    Context ct = new InitialContext();
                    dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                }
            }
        }
        Connection conn = dsCache.getConnection();
    }
}
通過以上優化,可以使你的封裝類以最低的資源消耗來獲取最大的性能.我們可以這樣來進行測試:
把if(dsCache == null){}發生時的情況記錄到日誌中,看看什麼時候發生了這種異常,我對我的數據庫進行了跟蹤,我們給青島日報社做的一臺郵件系統主機上有6萬多用戶,同時併發的訪問量很大,但日記記錄,只有每次重啓動應用的時候才產生一條記錄,也就是ConnectionFactory第一次調用時才產生日誌,其它情況非常正常:
public class ConnectionFactory{
    private static DataSource dsCache = null;
    private static Properties properties = new Properties();
    aMethodForGetConnectio(){
        if(dsCache == null){
            synchronized(this){
                if(dsCache==null){
                    if(!properties.containsKey("key"))
                        properties.load(new FileInputStream("配置文件"));
                    //只有在沒有找到屬性時纔去再讀配置文件
                    Context ct = new InitialContext();
                    dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                }
            }
            //當發生dsCache == null時,記錄下發生對該方法的調用者:
            PrintWriter pw = new PrintWriter(new FileWriter("connection.log",true));
            pw.println(new java.util.Date() + ":當前方法名如aMethodForGetConnectio():"
                    + sun.reflect.Reflection.getCallerClass(2));
            pw.close();
        }
        Connection conn = dsCache.getConnection();
    }
}
主要記錄當前日期,當前方法名稱和是哪個類調用了該方法.sun.reflect.Reflection.getCallerClass(int i)方法中,i爲0是Reflection本身,1是Bean或封裝類,就是ConnectionFactory,2就是調用它的類,3以上不確定.這樣你可以把你的封裝類或Bean進行一段時間(至少幾天以上)跟蹤,如果應用一直沒有重啓動,那就應該只產生一條記錄.這樣你就有了一個頂級性能的封裝類了.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章