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());
}

}

posted on 2012-08-27 15:24  刀光建影  阅读(3665)  评论(0编辑  收藏  举报

导航