C3P0連接池使用教程

轉載請註明原文地址:http://www.cnblogs.com/ygj0930/p/6405861.html

 在項目中的應用見: https://github.com/ygj0930/CoupleSpace

    一般我們在項目中操作數據庫時,都是每次需要操作數據庫就建立一個連接,操作完成後釋放連接。因爲jdbc沒有保持連接的能力,一旦超過一定時間沒有使用(大約幾百毫秒),連接就會被自動釋放掉。而每次新建連接都需要140毫秒左右的時間,所以耗費時間比較多。若使用C3P0連接池來池化連接,隨時取用,則平均每次取用只需要10-20毫秒。這在高併發隨機訪問數據庫的時候對效率的提升有很大幫助。

    C3P0連接池會根據你的配置來初始化N個數據庫連接,空閒T時間後連接過期又會自動新建K個連接使得連接池總有空閒的數據庫連接等待被取用。我們只需通過dataSourse.getConnection()即可從線程池中取用一個已經連接好的空閒連接,執行數據庫操作。然後“斷開”(放回)這個連接,把這個連接的使用權放回連接池。真正的數據庫連接的創建與釋放是由C3P0在後臺自動完成的,我們花的只是取用與釋放佔用權的時間。全程耗時10+毫秒,比原來提高了幾十倍。

    下面介紹一種C3P0的三層式使用。與一般的C3P0使用方式不同,三層式把數據庫操作分爲了三部分:C3P0操作類C3P0Utils、封裝了一次數據庫操作的VO對象DBUtils_BO、數據庫操作類DBUtils。下面一一講解:

    一:C3P0Utils:負責從連接池獲取一個連接、放回一個連接。

    這裏主要是C3P0連接池的使用。步驟如下:

    1:下載C3P0工具包:  https://sourceforge.net/projects/c3p0/files/latest/download?source=files

    解壓出來後得到3個jar包,導入你的項目中。

    2:對C3P0連接池進行配置。

    主要包括:初始化連接池時建立多少個連接、連接池最少多少個連接最多容納多少連接、每個連接的生存時間、連接池能同時允許多少個操作進行,以及對具體數據庫連接的配置:數據庫的驅動、數據庫的URL、數據庫登錄名、數據庫密碼、對這個數據庫的連接池的細化配置(比如初始化時建立多少連接,最多最少連接數等等)。一個數據庫的連接池配置用一個<named-config name="標識"> </name-config>節點來定義。在C3P0Utils中創建連接池時把  “標識”  作爲連接池的構造函數的參數傳入,則C3P0在配置文件中找到同名節點,按照這個節點的配置來創建相應配置的連接池。配置步驟如下:

    在src目錄下新建一個名叫  c3p0-config.xml  的文件,注意,必須是這個文件名。

    然後在這個文件中進行配置上面講到的各項:

複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
   <!--默認配置-->
    <default-config>  
        <property name="initialPoolSize">10</property>  
        <property name="maxIdleTime">30</property>  
        <property name="maxPoolSize">100</property>  
        <property name="minPoolSize">10</property>  
        <property name="maxStatements">200</property>  
    </default-config>  
  
   <!--配置連接池mysql-->
    <named-config name="mysql">  
        <property name="driverClass">com.mysql.jdbc.Driver</property>  
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/CoupleSpace</property>  
        <property name="user">root</property>  
        <property name="password">root</property>  
        <property name="initialPoolSize">10</property>  
        <property name="maxIdleTime">30</property>  
        <property name="maxPoolSize">100</property>  
        <property name="minPoolSize">10</property>  
        <property name="maxStatements">200</property>  
    </named-config>  
    
    <!--配置連接池2-->
    ......
    <!--配置連接池3-->
    ......
    <!--配置連接池4-->
    ......
</c3p0-config>
複製代碼

    更細緻化的有以下配置:

複製代碼
<!--acquireIncrement:鏈接用完了自動增量3個。 -->
    <property name="acquireIncrement">3</property>

    <!--acquireRetryAttempts:鏈接失敗後重新試30次。-->
    <property name="acquireRetryAttempts">30</property>
 
    <!--acquireRetryDelay;兩次連接中間隔1000毫秒。 -->
    <property name="acquireRetryDelay">1000</property>
 
    <!--autoCommitOnClose:連接關閉時默認將所有未提交的操作回滾。 -->
    <property name="autoCommitOnClose">false</property>
 
    <!--automaticTestTable:c3p0測試表,沒什麼用。-->
    <property name="automaticTestTable">Test</property>
 
    <!--breakAfterAcquireFailure:出錯時不把正在提交的數據拋棄。-->
    <property name="breakAfterAcquireFailure">false</property>
 
    <!--checkoutTimeout:100毫秒後如果sql數據沒有執行完將會報錯,如果設置成0,那麼將會無限的等待。 --> 
    <property name="checkoutTimeout">100</property>
 
    <!--connectionTesterClassName:通過實現ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
    <property name="connectionTesterClassName"></property>
 
    <!--factoryClassLocation:指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設置,默認null即可。-->
    <property name="factoryClassLocation">null</property>
 
    <!--forceIgnoreUnresolvedTransactions:作者強烈建議不使用的一個屬性。--> 
    <property name="forceIgnoreUnresolvedTransactions">false</property>
 
    <!--idleConnectionTestPeriod:每60秒檢查所有連接池中的空閒連接。--> 
    <property name="idleConnectionTestPeriod">60</property>
 
    <!--initialPoolSize:初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。 --> 
    <property name="initialPoolSize">3</property>
 
    <!--maxIdleTime:最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。-->
    <property name="maxIdleTime">60</property>
 
    <!--maxPoolSize:連接池中保留的最大連接數。 -->
    <property name="maxPoolSize">15</property>
 
    <!--maxStatements:最大鏈接數。-->
    <property name="maxStatements">100</property>
 
    <!--maxStatementsPerConnection:定義了連接池內單個連接所擁有的最大緩存statements數。Default: 0  -->
    <property name="maxStatementsPerConnection"></property>
 
    <!--numHelperThreads:異步操作,提升性能通過多線程實現多個操作同時被執行。Default: 3--> 
    <property name="numHelperThreads">3</property>
 
    <!--overrideDefaultUser:當用戶調用getConnection()時使root用戶成爲去獲取連接的用戶。主要用於連接池連接非c3p0的數據源時。Default: null--> 
    <property name="overrideDefaultUser">root</property>
 
    <!--overrideDefaultPassword:與overrideDefaultUser參數對應使用的一個參數。Default: null-->
    <property name="overrideDefaultPassword">password</property>
 
    <!--password:密碼。Default: null--> 
    <property name="password"></property>
 
    <!--preferredTestQuery:定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意: 測試的表必須在初始數據源的時候就存在。Default: null-->
    <property name="preferredTestQuery">select id from test where id=1</property>
 
    <!--propertyCycle:用戶修改系統配置參數執行前最多等待300秒。Default: 300 --> 
    <property name="propertyCycle">300</property>
 
    <!--testConnectionOnCheckout:因性能消耗大請只在需要的時候使用它。Default: false -->
    <property name="testConnectionOnCheckout">false</property>
 
    <!--testConnectionOnCheckin:如果設爲true那麼在取得連接的同時將校驗連接的有效性。Default: false -->
    <property name="testConnectionOnCheckin">true</property>
 
    <!--user:用戶名。Default: null-->
    <property name="user">root</property>
 
    <!--usesTraditionalReflectiveProxies:動態反射代理。Default: false-->
    <property name="usesTraditionalReflectiveProxies">false</property>
複製代碼

 (另:有人試過用properties文件來配置C3P0,通過setProperties(properties)方法並不能成功設置連接池的屬性,而需要一條條屬性去設置setXXX(properties.get(XX))才能成功設置。我的理解是:連接池本身有一個屬性叫做properties,然後調用setProperties()方法只是把配置文件賦值給了連接池的properties屬性而已,而不是讀取這個配置文件並把這個配置文件中的key-value對提取出來並set給連接池的相應屬性。所以,還需要手動加載這個文件,並且逐條屬性去設置給連接池。而我們這裏用xml文件去配置的話,C3P0創建連接池時是自動根據這個文件的配置去創建的,所以配置生效。)

    3:創建C3P0Utils類,定義獲取connection、釋放connection的方法

複製代碼
public class C3p0Utils {
    static org.apache.log4j.Logger logger=org.apache.log4j.Logger.getLogger(C3p0Utils.class.getName());
    
    //通過標識名來創建相應連接池
    static ComboPooledDataSource dataSource=new ComboPooledDataSource("mysql");
    //從連接池中取用一個連接
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
            
        } catch (Exception e) {
            logger.error("Exception in C3p0Utils!", e);
            throw new MyError("數據庫連接出錯!", e);            
        }
    }    
    //釋放連接回連接池
     public static void close(Connection conn,PreparedStatement pst,ResultSet rs){  
            if(rs!=null){  
                try {  
                    rs.close();  
                } catch (SQLException e) {  
                    logger.error("Exception in C3p0Utils!", e);
                    throw new MyError("數據庫連接關閉出錯!", e);            
                }  
            }  
            if(pst!=null){  
                try {  
                    pst.close();  
                } catch (SQLException e) {  
                    logger.error("Exception in C3p0Utils!", e);
                    throw new MyError("數據庫連接關閉出錯!", e);    
                }  
            }  
      
            if(conn!=null){  
                try {  
                    conn.close();  
                } catch (SQLException e) {  
                    logger.error("Exception in C3p0Utils!", e);
                    throw new MyError("數據庫連接關閉出錯!", e);    
                }  
            }  
        }  
}
複製代碼

    二:創建DBUtils_BO類來封裝一次數據庫操作

    BO類中主要封裝了一個數據庫連接、一個Statement/PreStatement(推薦,更安全)、一個結果集。這樣,在DAO層的一次數據庫操作就可封裝在這個bo對象裏,直接把這個對象傳給數據庫操作類執行數據庫操作即可。操作完成後的結果集也封裝在了這個bo對象裏,在需要提取結果的地方通過這個bo對象的rs字段進行結果提取即可。

複製代碼
public class DBUtil_BO {   
    public Connection conn = null;
    public PreparedStatement st = null;
    public ResultSet rs = null;
    public DBUtil_BO() {
        super();
    }    
}
複製代碼

   三:創建DBUtils數據庫操作類進行具體的數據庫操作

   數據庫操作類主要負責執行數據庫操作封裝類bo對象的操作以及釋放連接回連接池,並把結果封裝回bo對象。

複製代碼
public class DBUtils {
     static org.apache.log4j.Logger logger=org.apache.log4j.Logger.getLogger(DBUtils.class.getName());
     
     
     private static void realseSource( Connection _conn, PreparedStatement _st,ResultSet _rs){        
            C3p0Utils.close(_conn,_st,_rs);
        }

        public static void realseSource(DBUtil_BO _vo){    
            if(_vo!=null){
                realseSource(_vo.conn, _vo.st, _vo.rs);
            }        
        }
        //注意:查詢操作完成後,因爲還需提取結果集中信息,所以仍保持連接,在結果集使用完後才通過DBUtils.realseSource()手動釋放連接
        public static void executeQuery(DBUtil_BO vo)
        {        
            try{
                vo.rs = vo.st.executeQuery();
            }catch (SQLException e){            
                realseSource(vo);
                String uuid=Uuid.create().toString();
                logger.error("UUID:"+uuid+", SQL語法有誤: ",e);
                throw new MyException("err.user.dao.jdbc",e,uuid);
            }    
        }
        
       //而update操作完成後就可以直接釋放連接了,所以在方法末尾直接調用了realseSourse()
        public static  void executeUpdate(DBUtil_BO vo)
        {

            Connection conn = vo.conn;
            PreparedStatement st = vo.st;
            try {
                st.executeUpdate();
            } catch (SQLException e) {
                realseSource(conn, st, null);        
                String uuid=Uuid.create().toString();
                logger.error("UUID:"+uuid+", SQL語法有誤: ",e);
                throw new MyException("err.user.dao.jdbc",e,uuid);
            }
            realseSource(conn, st,null );                

        }
}
複製代碼

    四:在項目中使用連接池

    主要分5步:

    1:創建連接池datasourse、bo、utils對象

    2:bo對象的連接是在連接池獲取的,所以bo.conn=C3P0Utils.getConnection();

    3:定義數據庫操作語句sql,並用bo對象的preparestatement預處理

    4:把封裝了數據庫操作的bo對象傳給utils對象執行數據庫操作

    5:bo對象釋放連接,以便下一次獲取連接。(查詢操作的需要顯示釋放)

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

 

發佈了0 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章