c3p0連接池用法


  使用連接池的時候並不是在代碼中不用獲取/釋放數據庫連接,而是在代碼中向連接池申請/釋放連接,對於代碼而言,可以把連接池看成數據庫。

換句話說,連接池就是數據庫的代理,之所以要使用這個代理是因為直接向數據庫申請/釋放連接是要降低性能的:如果每一次數據訪問請求都必須經歷建立數據庫連接、打開數據庫、存取數據和關閉數據庫連接等步驟,而連接並打開數據庫是一件既消耗資源又費時的工作,那么頻繁發生這種數據庫操作時,系統的性能必然會急劇下降。
連接池的作用是自己維護數據庫連接,數據庫連接池的主要操作如下:
  (1)建立數據庫連接池對象(服務器啟動)。
  (2)按照事先指定的參數創建初始數量的數據庫連接(即:空閑連接數)。
  (3)對於一個數據庫訪問請求,直接從連接池中得到一個連接。如果數據庫連接池對象中沒有空閑的連接,且連接數沒有達到最大(即:最大活躍連接數),創建一個新的數據庫連接。
  (4)存取數據庫。
  (5)關閉數據庫,釋放所有數據庫連接(此時的關閉數據庫連接,並非真正關閉,而是將其放入空閑隊列中。如實際空閑連接數大於初始空閑連接數則釋放連接)。
  (6)釋放數據庫連接池對象(服務器停止、維護期間,釋放數據庫連接池對象,並釋放所有連接)。

  從連接池獲取的連接connection跟JDK中的connection有點不同,前者的close方法並沒有關閉與數據庫的連接,而是將連接返回到池中,這樣就可以復用了。如果不調用close方法的話拿就失去了使用連接池的意義了。

  開源連接池有很多:DBCP、C3P0、Proxool 、 BoneCP等

  C3P0是一個開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發布,包括了實現jdbc3和jdbc2擴展規范說明的Connection 和Statement 池的DataSources 對象。

  1. 下載c3p0的jar,並添加log4j.jar.
  2. 采用ThreadLocal線程局部變量保證線程安全.

使用連接池和不使用連接池時的性能差異簡單的C3P0使用測試示例

package com.lnbdqn;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public final class ConnectionManager {

    private static ConnectionManager instance;
    private static ComboPooledDataSource dataSource;

    private ConnectionManager() throws SQLException, PropertyVetoException {
        dataSource = new ComboPooledDataSource();

        dataSource.setUser("loux");
        dataSource.setPassword("loux");
        dataSource.setJdbcUrl("jdbc:oracle:thin:@192.168.100.70:1521:orcl");
        dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");
        dataSource.setInitialPoolSize(5);
        dataSource.setMinPoolSize(1);
        dataSource.setMaxPoolSize(10);
        dataSource.setMaxStatements(50);
        dataSource.setMaxIdleTime(60);
    }

    public static final ConnectionManager getInstance() {
        if (instance == null) {
            try {
                instance = new ConnectionManager();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return instance;
    }

    public synchronized final Connection getConnection() {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}
package com.lnbdqn;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import oracle.jdbc.pool.OracleDataSource;

public class ConnectionDemo {

    public static void main(String[] args) throws SQLException {
        System.out.println("使用連接池................................");
        for (int i = 0; i < 20; i++) {
            long beginTime = System.currentTimeMillis();
            Connection conn = ConnectionManager.getInstance().getConnection();
            try {
                PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM t_fmscpy200");
                ResultSet rs = pstmt.executeQuery();
                while (rs.next()) {
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            long endTime = System.currentTimeMillis();
            System.out.println("第" + (i + 1) + "次執行花費時間為:" + (endTime - beginTime));
        }

        System.out.println("不使用連接池................................");
        for (int i = 0; i < 20; i++) {
            long beginTime = System.currentTimeMillis();
            OracleDataSource ods = new OracleDataSource();
            ods.setUser("loux");
            ods.setPassword("loux");
            ods.setURL("jdbc:oracle:thin:@192.168.100.70:1521:orcl");
            Connection conn = ods.getConnection();
            try {
                PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM table_name");
                ResultSet rs = pstmt.executeQuery();
                while (rs.next()) {
                                    // do nothing...
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("第" + (i + 1) + "次執行花費時間為:"
                                + (endTime - beginTime));
        }

    }
}
控制台輸出的結果為:

使用連接池................................
第1次執行花費時間為:1469
第2次執行花費時間為:0
第3次執行花費時間為:16
第4次執行花費時間為:0
第5次執行花費時間為:0
第6次執行花費時間為:15
第7次執行花費時間為:0
第8次執行花費時間為:0
第9次執行花費時間為:0
第10次執行花費時間為:0
第11次執行花費時間為:16
第12次執行花費時間為:0
第13次執行花費時間為:0
第14次執行花費時間為:0
第15次執行花費時間為:0
第16次執行花費時間為:16
第17次執行花費時間為:0
第18次執行花費時間為:0
第19次執行花費時間為:15
第20次執行花費時間為:0
不使用連接池................................
第1次執行花費時間為:47
第2次執行花費時間為:31
第3次執行花費時間為:32
第4次執行花費時間為:46
第5次執行花費時間為:32
第6次執行花費時間為:31
第7次執行花費時間為:47
第8次執行花費時間為:31
第9次執行花費時間為:47
第10次執行花費時間為:31
第11次執行花費時間為:47
第12次執行花費時間為:31
第13次執行花費時間為:32
第14次執行花費時間為:46
第15次執行花費時間為:47
第16次執行花費時間為:32
第17次執行花費時間為:46
第18次執行花費時間為:47
第19次執行花費時間為:32
第20次執行花費時間為:31
可以看出,在使用連接池時,第一次執行花費的時間稍長,因為第一次初始化操作需要創建多個連接並放入池中,以后使用時將會大大縮短執行時間。
在不使用連接池時,每次花費的時間都比較長。

下面是一個service層的銀行轉賬方法

    public void transferMoneyNew(int from,int to,float money) throws Exception{
        AccountDAOImpl dao = null;
        try{
            /*完成的功能:
            1.從數據源中獲取Connection
            2.開啟事務
            3. 放到線程上
            */
            JDBCUtils.startTransaction();  
            //創建DAO
            dao = new AccountDAOImpl();     
            //獲取賬戶信息
            Account fromAccount = dao.findAccountByID(from);
            Account toAccount = dao.findAccountByID(to);
            //扣錢和加錢
            fromAccount.setMoney(fromAccount.getMoney() - money);
            toAccount.setMoney(toAccount.getMoney() + money);        
            //更新數據庫
            dao.updateAccount(fromAccount);       
            //產生錯誤
            int i=1/0;       
            dao.updateAccount(toAccount);        
            //提交
            JDBCUtils.commit();
        }catch(Exception ex){
            JDBCUtils.rollback();
            throw new ServiceException(ex);
        }finally{
            JDBCUtils.release();
        }
    }
////////////////////////////////////////////////////////////////////////////////////
JDBCUtils類
public class JDBCUtils {
    //連接的容器
    public static ThreadLocal<Connection> container = new ThreadLocal<Connection>();
    
    //定義c3p0 數據源
    private static DataSource ds = new ComboPooledDataSource();
    
    /*完成的功能:
    1.從數據源中獲取Connection
    2.開啟事務
    3. 放到線程上
    */
    public static void startTransaction() throws SQLException{
        Connection conn  = container.get();
        //當前線程上是否已經存在連接
        if(conn == null){
            conn = ds.getConnection();
        }
        //開啟事務
        conn.setAutoCommit(false);
        //放到當前線程上
        container.set(conn);
    }
    
    //提交當前線程上的連接
    public static void commit() throws SQLException{
        Connection conn  = container.get();
        if(conn != null){
            conn.commit();
        }
    }
        //回滾當前線程上的連接
    public static void  rollback() throws SQLException{
        Connection conn  = container.get();
        if(conn != null){
            conn.rollback();
        }
    }
    //釋放當前線程上的連接
    public static void release() throws SQLException{
        Connection conn  = container.get();
        if(conn != null){
            //從當前線程上,拿掉連接
            container.remove();
            conn.close();
        }        
    }
    //返回數據源
    public static DataSource getDataSource(){
        return ds;
    }
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    //釋放資源
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
             }finally{
                 rs = null;
             }
        }   
        if(st != null){
            try {
                st.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
             }finally{
                 st = null;
             }
        }
          if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
             }finally{
                 conn = null;
             }
        }        
    }
}

OSChina 的 DBManager 類 管理數據庫連接

public class DBManager {

    private final static Log log = LogFactory.getLog(DBManager.class);
    private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
    private static DataSource dataSource;
    private static boolean show_sql = false;
    
    static {
        initDataSource(null);
    }

    /**
     * 初始化連接池
     * @param props
     * @param show_sql
     */
    private final static void initDataSource(Properties dbProperties) {
        try {
            if(dbProperties == null){
                dbProperties = new Properties();
                dbProperties.load(DBManager.class.getResourceAsStream("db.properties"));
            }
            Properties cp_props = new Properties();
            for(Object key : dbProperties.keySet()) {
                String skey = (String)key;
                if(skey.startsWith("jdbc.")){
                    String name = skey.substring(5);
                    cp_props.put(name, dbProperties.getProperty(skey));
                    if("show_sql".equalsIgnoreCase(name)){
                        show_sql = "true".equalsIgnoreCase(dbProperties.getProperty(skey));
                    }
                }
            }
            dataSource = (DataSource)Class.forName(cp_props.getProperty("datasource")).newInstance();
            if(dataSource.getClass().getName().indexOf("c3p0")>0){
                //Disable JMX in C3P0
                System.setProperty("com.mchange.v2.c3p0.management.ManagementCoordinator", 
                        "com.mchange.v2.c3p0.management.NullManagementCoordinator");
            }
            log.info("Using DataSource : " + dataSource.getClass().getName());
            BeanUtils.populate(dataSource, cp_props);

            Connection conn = getConnection();
            DatabaseMetaData mdm = conn.getMetaData();
            log.info("Connected to " + mdm.getDatabaseProductName() + 
                              " " + mdm.getDatabaseProductVersion());
            closeConnection();
        } catch (Exception e) {
            throw new DBException(e);
        }
    }
    
    /**
     * 斷開連接池
     */
    public final static void closeDataSource(){
        try {
            dataSource.getClass().getMethod("close").invoke(dataSource);
        } catch (NoSuchMethodException e){ 
        } catch (Exception e) {
            log.error("Unabled to destroy DataSource!!! ", e);
        }
    }

    public final static Connection getConnection() throws SQLException {
        Connection conn = conns.get();
        if(conn ==null || conn.isClosed()){
            conn = dataSource.getConnection();
            conns.set(conn);
        }
        return (show_sql && !Proxy.isProxyClass(conn.getClass()))?
                      new _DebugConnection(conn).getConnection():conn;
    }
    
    /**
     * 關閉連接
     */
    public final static void closeConnection() {
        Connection conn = conns.get();
        try {
            if(conn != null && !conn.isClosed()){
                conn.setAutoCommit(true);
                conn.close();
            }
        } catch (SQLException e) {
            log.error("Unabled to close connection!!! ", e);
        }
        conns.set(null);
    }

    /**
     * 用於跟蹤執行的SQL語句
     * @author Winter Lau
     */
    static class _DebugConnection implements InvocationHandler {
        
        private final static Log log = LogFactory.getLog(_DebugConnection.class);
        
        private Connection conn = null;

        public _DebugConnection(Connection conn) {
            this.conn = conn;
        }

        /**
         * Returns the conn.
         * @return Connection
         */
        public Connection getConnection() {
            return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), 
                             conn.getClass().getInterfaces(), this);
        }
        
        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
            try {
                String method = m.getName();
                if("prepareStatement".equals(method) || "createStatement".equals(method))
                    log.info("[SQL] >>> " + args[0]);                
                return m.invoke(conn, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }

    }
    
}
# DataSource
jdbc.datasource=com.mchange.v2.c3p0.ComboPooledDataSource
jdbc.show_sql=true

# Database Configurations
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/oscdb
jdbc.user=root
jdbc.password=xxxx
jdbc.maxPoolSize=100
jdbc.minPoolSize=2
jdbc.initialPoolSize=2
jdbc.acquireIncrement=2
jdbc.maxStatements=1000
jdbc.maxIdleTime=300
jdbc.checkoutTimeout=5000

參數配置例子

package com.wb.db;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 采用ThreadLocal線程局部變量保證線程安全
* @author hemes1314
*/
public class C3p0Pool {

public static ThreadLocal connectionHolder = new ThreadLocal(); 

private static DataSource dataSource;

    public C3p0Pool(){   
    }   

    public static Connection getConnection() {
    
    Connection conn = (Connection) connectionHolder.get();
    //如果在當前線程中沒有綁定相應的Connection
    if(conn==null){
         if (dataSource == null) {   
             initDataSource();
         }   
         try {   
             conn = dataSource.getConnection(); 
             //將Connection設置到ThreadLocal線程變量中
             connectionHolder.set(conn); 
         } catch (SQLException e) {   
             // TODO Auto-generated catch block   
             e.printStackTrace();   
         } 
    }
        return conn;   
    }
    
    public static void closeConnection(){
    Connection conn = (Connection) connectionHolder.get();
    if(conn!=null){
       try {
     conn.close();
     //從ThreadLocal中清除Connection
     connectionHolder.remove();
    } catch (SQLException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    }
    }
    
    public static void initDataSource(){   
     String driverClassName=null;   
        String url=null;   
        String username=null;   
        String password=null;   
        int initialPoolSize=3;   
        int maxPoolSize=15;
        int minPoolSize=5;
        int acquireRetryDelay=1000;
        int maxIdleTime=60;

    Configuration config=new Configuration("oraConn.properties");
        driverClassName = config.getValue("driver");   
        url = config.getValue("url");
        username = config.getValue("user");
        password = config.getValue("password");   

        initialPoolSize = Integer.parseInt(config.getValue("initialPoolSize").trim());       
        maxPoolSize = Integer.parseInt(config.getValue("maxPoolSize").trim());
        minPoolSize = Integer.parseInt(config.getValue("minPoolSize").trim());
        maxIdleTime = Integer.parseInt(config.getValue("maxIdleTime").trim()); 

        ComboPooledDataSource cpds = new ComboPooledDataSource();    
        try {
    cpds.setDriverClass(driverClassName);
   } catch (PropertyVetoException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
        cpds.setUser(username);   
    cpds.setPassword(password);   
    cpds.setJdbcUrl(url);
    //初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default: 3 initialPoolSize   
    cpds.setInitialPoolSize(initialPoolSize);   
    //連接池中保留的最大連接數。Default: 15 maxPoolSize   
    cpds.setMaxPoolSize(maxPoolSize);
    //連接池中保留的最小連接數。   
    cpds.setMinPoolSize(minPoolSize);
    //獲得連接的最大等待毫秒數。Default: 1000 acquireRetryDelay
    cpds.setAcquireRetryDelay(acquireRetryDelay);
    //最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 maxIdleTime   
    cpds.setMaxIdleTime(maxIdleTime);
    
    
    //當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 acquireIncrement   
    //cpds.setAcquireIncrement(3);   
    
    //每60秒檢查所有連接池中的空閑連接。Default: 0 idleConnectionTestPeriod   
    //cpds.setIdleConnectionTestPeriod(60);
    
    //連接關閉時默認將所有未提交的操作回滾。Default: false autoCommitOnClose   
    //cpds.setAutoCommitOnClose(true);
    
    //JDBC的標准參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0 
    //cpds.setMaxStatements(1);
    //maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數
    //cpds.setMaxStatementsPerConnection(100);
    //定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意:測試的表必須在初始數據源的時候就存在。Default: null preferredTestQuery   
    //cpds.setPreferredTestQuery("select sysdate from dual");   
    
    // 因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的   
    // 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable   
    // 等方法來提升連接測試的性能。Default: false testConnectionOnCheckout   
    //cpds.setTestConnectionOnCheckout(true);
    
    //如果設為true那么在取得連接的同時將校驗連接的有效性。Default: false testConnectionOnCheckin   
    //cpds.setTestConnectionOnCheckin(true);   
       
    //定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 acquireRetryAttempts   
    //cpds.setAcquireRetryAttempts(30);     
    
    //獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效   
    //保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試   
    //獲取連接失敗后該數據源將申明已斷開並永久關閉。Default: false breakAfterAcquireFailure   
    //cpds.setBreakAfterAcquireFailure(false);   
    dataSource = cpds;        
    }
    
    /* 用於測試連接狀態的方法*/
    public static void main(String[] args) {
    ComboPooledDataSource ds=(ComboPooledDataSource)dataSource;   
     try {
    System.out.println(ds.getConnection());
   } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
     //System.out.println(ds.getInitialSize());   
     //System.out.println(ds.getNumActive());   
     //System.out.println(ds.getNumIdle());   
     //System.out.println(ds.getDefaultAutoCommit());
}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM