四个流行的java连接池之DBCP篇

DBCP (DatabaseConnection Pool)是一个依赖Jakarta commons-pool对象池机制的数据库连接池,Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版本对应的是JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了,功能上倒是没有什么区别。

使用DBCP会用到3个包:即commons-dbcp.jarcommons-pool.jarcommons-collections.jar.

使用DBCP的好处:

   建立数据库连接是一个非常消耗资源的行为,如当有一个数据访问操作时,需要创建一个连接,当这次服务访问完成后,这个连接虽然已关闭但是还没有被销毁,这样会耗内存。当下次再有需要数据访问的操作时程序又会创建连接,如此以往既消耗时间,又要消耗资源。所以最后就使用了一种节省资源的方式,即数据库连接池的方式。DBCP的工作原理是,程序首先会初始化相应的数据库连接池,以供程序访问,当某个操作需要访问数据库时,程序会首先在连接池中取得空闲连接,如没有空闲连接在创建,用完之后归还连接池,这样达到了连接的重利用,不用频繁的创建连接和销毁,从而优化程序。

常见属性:

driver:数据库驱动,如com.mysql.jdbc.Driver

url:数据库连接,如jdbc:mysql://localhost:3306/testDB

username:数据库用户名,如root

password:数据库用户名口令,如root

initsize:初始化连接池大小

minidle:最小空闲连接

maxidle:最大空闲连接

它表示的是如果在并发的情况下,达到了maxidle=15一下面配置为例,这时连接池就必须从数据库中获取15个连接来供应程序使用,当应用连接关闭之后,由于maxikle=10并不是所有的连接都归还给了数据库,将会有10连接保存在连接池中,这时的状态就是空闲。

maxactive:最大连接,表示在并发情况下最大能从数据连接池中获取的连接数

maxwait:最大等待时间

removeabandoned:是否自动回收超时连接,boolean类型(true/false

dbcp1.2.2之后有新的支持,在这个版本中表示此属性已过期。

removeabandonedtimeout:超时时间,以秒为单位

示例(Java操作):

package com.wgy.dbcp;

import java.io.IOException;

import java.io.InputStream;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;

importorg.apache.commons.dbcp.BasicDataSource;

public class DBCPTest {

// 连接驱动

private static String DRIVER_CLASS =null;

// 连接地址

private static String URL = null;

// 数据库用户名

private static String USER_NAME =null;

// 数据库口令

private static String PASSWORD =null;

protected Connection conn = null;

protected PreparedStatement ps =null;

protected ResultSet rs = null;

// 初始化连接

private static int initSize;

// 最大连接数

private static int maxActive;

// 最小空闲连接

private static int minIdle;

// 最大空闲连接

private static int maxIdle;

// 最大等待时间

private static int maxWait;

// 等待超时是否自动回收超时连接

private static booleanremoveAbandoned;

// 超时时间

private static intremeoveAbandonedTimeout;

// 是否事物提交

private static booleandefaultAutocommit;

// 对于数据库是否只能读取

private static booleandefaultReadonly;

// 创建数据库连接对象(数据源)

private static BasicDataSourcedataSource = new BasicDataSource();

// 配置数据源

static {

setProperty();

DataSourceConfig();

}

/**

 * 读取配置文件,并设置数据库连接池的属性

 */

private static void setProperty() {

// 创建配置文件对象

Properties props = new Properties();

// 获得配置文件输入流对象

InputStream input = DBCPTest.class

.getResourceAsStream("/dbcp.properties");

try {

// 将文件读取到内存中

props.load(input);

// 根据键获得值

DRIVER_CLASS =props.getProperty("driver");

System.out.println(props.getProperty("driver"));

props.getProperty("url");

 

USER_NAME =props.getProperty("userName");

PASSWORD =props.getProperty("password");

initSize =Integer.parseInt(props.getProperty("initsize"));

maxActive =Integer.parseInt(props.getProperty("maxactive"));

minIdle =Integer.parseInt(props.getProperty("minidle"));

maxIdle =Integer.parseInt(props.getProperty("maxidle"));

maxWait =Integer.parseInt(props.getProperty("maxwait"));

removeAbandoned =Boolean.parseBoolean(props

.getProperty("removeabandoned"));

remeoveAbandonedTimeout =Integer.parseInt(props

.getProperty("removeabandonedtimeout"));

defaultAutocommit =Boolean.parseBoolean(props

.getProperty("defaultautocommit"));

defaultReadonly =Boolean.parseBoolean(props

.getProperty("defaultreadonly"));

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

input.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

/**

 * 设置dataSource各属性值

 */

private static voidDataSourceConfig() {

// 设置数据库驱动

dataSource.setDriverClassName(DRIVER_CLASS);

// 设置URL地址

dataSource.setURL);< /font>

// 设置用户名

dataSource.setUsername(USER_NAME);

// 设置用户口令

dataSource.setPassword(PASSWORD);

// 初始化连接

dataSource.setInitialSize(initSize);

// 设置最大连接数

dataSource.setMaxActive(maxActive);

// 设置最小空闲连接

dataSource.setMinIdle(minIdle);

// 设置最大空闲连接

dataSource.setMaxIdle(maxIdle);

// 设置最大等待时间

dataSource.setMaxWait(maxWait);

// 设置是否自动回收

dataSource.setRemoveAbandoned(removeAbandoned);

// 设置超时时间

dataSource.setRemoveAbandonedTimeout(remeoveAbandonedTimeout);

// 设置是否事物提交值

dataSource.setDefaultAutoCommit(defaultAutocommit);

// 设置对于数据库是否只读

dataSource.setDefaultReadOnly(defaultReadonly);

}

/**

 * 获得连接对象

 * 

 * @return

 */

public Connection getConn() {

try {

// 从连接池中获得连接对象

if (conn == null) {

conn = dataSource.getConnection();

}

} catch (SQLException e) {

e.printStackTrace();

}

return conn;

}

 

public ResultSet executeQuery(String sql,Object[] params) throws Exception {

ps = getConn().prepareStatement(sql);

if (params != null && params.length> 0) {

for (int i = 0; i < params.length; i++) {

ps.setObject(i + 1, params[i]);

}

}

return ps.executeQuery();

}

}

dbcp.properties文件:

driver=org.firebirdsql.jdbc.FBDriver

jdbc:firebirdsql:localhost /3050:D:/test.gdb

userName=SYSDBA

password=masterkey

initsize=5

maxactive=15

minidle=0

maxidle=10

maxwait=1000

removeabandoned=false

removeabandonedtimeout=180

apachedbcp来建立独立的数据库连接池(dbconnection pool)
数据库连接池的好处是不言而喻的,现在大部分的application server都提供自己的数据库连接池方案,此时,只要按照application server的文档说明,正确配置,即可在应用中享受到数据库连接池的好处。
但是,有些时候,我们的应用是个独立的java application,并不是普通的WEB/J2EE应用,而且是单独运行的,不要什么applicationserver的配合,这种情况下,我们就需要建立自己的数据库连接池方案了。这里,介绍如何利用apachedbcp来建立为民自己的数据库连接池。
1
。首先,下载必须的jar
dbcp
包,目前版本是1.2.1http://jakarta.apache.org/commons/dbcp/ 
pool包,目前版本是1.3http://jakarta.apache.org/commons/pool/ 
如果下载的pool包是1.2的版本,还要下载common-collections包:http://jakarta.apache.org/commons/collections/
在建立我们自己的数据库连接池时,可以使用xml文件来传入需要的参数,这里只使用hardcode的方式来简单介绍,所有需要我们自己写的代码很少,只要建立一个文件如下:

importorg.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import java.sql.SQLException;
import java.sql.Connection;
import java.util.Properties;

public class ConnectionSource {
    private static BasicDataSource dataSource = null;

    publicConnectionSource() {
    }

    publicstatic void init() {

       if (dataSource != null) {
            try {
               dataSource.close();
            } catch(Exception e) {
               //
            }
            dataSource =null;
        }

       try {
            Properties p= new Properties();
           p.setProperty("driverClassName","oracle.jdbc.driver.OracleDriver");
           p.setProperty("url","jdbc:oracle:thin:@192.168.0.1:1521:testDB");
           p.setProperty("password", "scott");
           p.setProperty("username", "tiger");
           p.setProperty("maxActive", "30");
           p.setProperty("maxIdle", "10");
           p.setProperty("maxWait", "1000");
           p.setProperty("removeAbandoned", "false");
           p.setProperty("removeAbandonedTimeout", "120");
           p.setProperty("testOnBorrow", "true");
           p.setProperty("logAbandoned", "true");

           dataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(p);

       } catch (Exception e) {
            //
        }
    }


    public static synchronized Connection getConnection()throws  SQLException {
        if (dataSource == null) {
            init();
        }
        Connection conn = null;
        if (dataSource != null) {
            conn =dataSource.getConnection();
        }
        return conn;
    }
}

接下来,在我们的应用中,只要简单地使用ConnectionSource.getConnection()就可以取得连接池中的数据库连接,享受数据库连接带给我们的好处了。当我们使用完取得的数据库连接后,只要简单地使用connection.close()就可把此连接返回到连接池中,至于为什么不是直接关闭此连接,而是返回给连接池,这是因为dbcp使用委派模型来实现Connection接口了。

在使用Properties来创建BasicDataSource时,有很多参数可以设置,比较重要的还有:

testOnBorrowtestOnReturntestWhileIdle,他们的意思是当是取得连接、返回连接或连接空闲时是否进行有效性验证(即是否还和数据库连通的),默认都为false。所以当数据库连接因为某种原因断掉后,再从连接池中取得的连接,实际上可能是无效的连接了,所以,为了确保取得的连接是有效的,可以把把这些属性设为true。当进行校验时,需要另一个参数:validationQuery,对oracle来说,可以是:SELECT COUNT(*) FROM DUAL,实际上就是个简单的SQL语句,验证时,就是把这个SQL语句在数据库上跑一下而已,如果连接正常的,当然就有结果返回了。

还有2个参数:timeBetweenEvictionRunsMillis minEvictableIdleTimeMillis他们两个配合,可以持续更新连接池中的连接对象,当timeBetweenEvictionRunsMillis 大于0时,每过timeBetweenEvictionRunsMillis时间,就会启动一个线程,校验连接池中闲置时间超过minEvictableIdleTimeMillis的连接对象。

在配置时,主要难以理解的主要有:removeAbandonedlogAbandonedremoveAbandonedTimeoutmaxWait这四个参数,设置了rmoveAbandoned=true那么在getNumActive()快要到getMaxActive()的时候,系统会进行无效的Connection的回收,回收的ConnectionremoveAbandonedTimeout(默认300)中设置的秒数后没有使用的Connection,激活回收机制好像是getNumActive()=getMaxActive()-2 :) 有点忘了。
logAbandoned=true的话,将会在回收事件后,在log中打印出回收Connection的错误信息,包括在哪个地方用了Connection却忘记关闭了,在调试的时候很有用。
  在这里私人建议maxWait的时间不要设得太长,maxWait如果设置太长那么客户端会等待很久才激发回收事件。
  以下是我的配置的properties文件:
#
连接设置
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:DBSERVER
jdbc.username=user
jdbc.password=pass

#
dataSource.initialSize=10

#
dataSource.maxIdle=20

#
dataSource.minIdle=5

#最大连接数量
dataSource.maxActive=50

#是否在自动回收超时连接的时候打印连接的超时错误
dataSource.logAbandoned=true

#是否自动回收超时连接
dataSource.removeAbandoned=true

#超时时间(以秒数为单位)
dataSource.removeAbandonedTimeout=180

#
dataSource.maxWait=1000

  以下是我在连接控制中调用的方法:

       Properties  dbProps=null;
//下面的读取配置文件可以根据实际的不同修改
        dbProps =ConfigProperties.getInstance().getProperties("jdbc.properties");
        try {
         String driveClassName =dbProps.getProperty("jdbc.driverClassName");
         String url =dbProps.getProperty("jdbc.url");
         String username =dbProps.getProperty("jdbc.username");
         String password =dbProps.getProperty("jdbc.password");
         

         String initialSize =dbProps.getProperty("dataSource.initialSize");
         String minIdle =dbProps.getProperty("dataSource.minIdle");
         String maxIdle =dbProps.getProperty("dataSource.maxIdle");
         String maxWait =dbProps.getProperty("dataSource.maxWait");
         String maxActive = dbProps.getProperty("dataSource.maxActive");
           //是否在自动回收超时连接的时候打印连接的超时错误
          boolean logAbandoned =(Boolean.valueOf(dbProps.getProperty("dataSource.logAbandoned","false"))).booleanValue();

         //是否自动回收超时连接
          boolean removeAbandoned= (Boolean.valueOf(dbProps.getProperty("dataSource.removeAbandoned","false"))).booleanValue();

         //超时时间(以秒数为单位)
          intremoveAbandonedTimeout =Integer.parseInt(dbProps.getProperty("dataSource.removeAbandonedTimeout","300"));
        

         dataSource = newBasicDataSource();
        dataSource.setDriverClassName(driveClassName);
         dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        //初始化连接数
         if(initialSize!=null)
         dataSource.setInitialSize(Integer.parseInt(initialSize));
         

         //最小空闲连接
         if(minIdle!=null)
         dataSource.setMinIdle(Integer.parseInt(minIdle));

        //最大空闲连接
         if(maxIdle!=null)
          dataSource.setMaxIdle(Integer.parseInt(maxIdle));
         

         //超时回收时间(以毫秒为单位)
         if(maxWait!=null)
         dataSource.setMaxWait(Long.parseLong(maxWait));
         

         //最大连接数
         if(maxActive!=null){
         if(!maxActive.trim().equals("0"))
          dataSource.setMaxActive(Integer.parseInt(maxActive));
         }

        System.out.println("logAbandoned="+logAbandoned);
           dataSource.setLogAbandoned(logAbandoned);
        dataSource.setRemoveAbandoned(removeAbandoned);
         dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
         

         Connection conn =dataSource.getConnection();
         if(conn==null){
          log("创建连接池时,无法取得连接!检查设置!!!");
         }else{
          conn.close();
         }
         System.out.println("
连接池创建成功!!!");
        }
        catch (Exception e) {
         e.printStackTrace();
           System.out.println("
创建连接池失败!请检查设置!!!");
        }

 

 

使用datasource的代码例子,

/*

2      * Licensed to the ApacheSoftware Foundation (ASF) under one or more

3      * contributor licenseagreements.  See the NOTICE filedistributed with

4      * this work for additionalinformation regarding copyright ownership.

5      * The ASF licenses thisfile to You under the Apache License, Version 2.0

6      * (the"License"); you may not use this file except in compliance with

7      * the License.  You may obtain a copy of the License at

8      *

9      *     http://www.apache.org/licenses/LICENSE-2.0

10     *

11     * Unless required byapplicable law or agreed to in writing, software

12     * distributed under theLicense is distributed on an "AS IS" BASIS,

13     * WITHOUT WARRANTIES ORCONDITIONS OF ANY KIND, either express or implied.

14     * See the License for the specificlanguage governing permissions and

15     * limitations under theLicense.

16     */

17    

18     import javax.sql.DataSource;

19     import java.sql.Connection;

20     import java.sql.Statement;

21     import java.sql.ResultSet;

22     importjava.sql.SQLException;

23    

24     //

25     // Here are thedbcp-specific classes.

26     // Note that they are onlyused in the setupDataSource

27     // method. In normal use,your classes interact

28     // only with the standardJDBC API

29     //

30     importorg.apache.commons.pool2.ObjectPool;

31     import org.apache.commons.pool2.impl.GenericObjectPool;

32     importorg.apache.commons.dbcp2.ConnectionFactory;

33     importorg.apache.commons.dbcp2.PoolingDataSource;

34     importorg.apache.commons.dbcp2.PoolableConnectionFactory;

35     importorg.apache.commons.dbcp2.DriverManagerConnectionFactory;

36    

37     //

38     // Here's a simple exampleof how to use the PoolingDataSource.

39     //

40    

41     //

42     // Note that this example isvery similar to the PoolingDriver

43     // example.  In fact, you could use the same pool in botha

44     // PoolingDriver and aPoolingDataSource

45     //

46    

47     //

48     // To compile this example,you'll want:

49     //  * commons-pool-1.5.4.jar

50     //  * commons-dbcp-1.2.2.jar

51     //  * j2ee.jar (for the javax.sql classes)

52     // in your classpath.

53     //

54     // To run this example,you'll want:

55     //  * commons-pool-1.5.6.jar

56     //  * commons-dbcp-1.3.jar (JDK 1.4-1.5) orcommons-dbcp-1.4 (JDK 1.6+)

57     //  * j2ee.jar (for the javax.sql classes)

58     //  * the classes for your (underlying) JDBCdriver

59     // in your classpath.

60     //

61     // Invoke the class usingtwo arguments:

62     //  * the connect string for your underlying JDBCdriver

63     //  * the query you'd like to execute

64     // You'll also want toensure your underlying JDBC driver

65     // is registered.  You can use the "jdbc.drivers"

66     // property to do this.

67     //

68     // For example:

69     //  java-Djdbc.drivers=oracle.jdbc.driver.OracleDriver \

70     //       -classpathcommons-pool-1.5.6.jar:commons-dbcp-1.4.jar:j2ee.jar:oracle-jdbc.jar:. \

71     //       PoolingDataSourceExample \

72     //      "jdbc:oracle:thin:scott/tiger@myhost:1521:mysid" \

73     //       "SELECT * FROM DUAL"

74     //

75     public classPoolingDataSourceExample {

76    

77         public static void main(String[] args) {

78             //

79             // First we load the underlying JDBCdriver.

80             // You need this if you don't use thejdbc.drivers

81             // system property.

82             //

83             System.out.println("Loadingunderlying JDBC driver.");

84             try {

85                Class.forName("oracle.jdbc.driver.OracleDriver");

86             } catch (ClassNotFoundException e) {

87                 e.printStackTrace();

88             }

89             System.out.println("Done.");

90    

91             //

92             // Then, we set up thePoolingDataSource.

93             // Normally this would be handledauto-magically by

94             // an external configuration, but inthis example we'll

95             // do it manually.

96             //

97             System.out.println("Setting updata source.");

98             DataSource dataSource =setupDataSource(args[0]);

99             System.out.println("Done.");

100   

101            //

102            // Now, we can use JDBC DataSource aswe normally would.

103            //

104            Connection conn = null;

105            Statement stmt = null;

106            ResultSet rset = null;

107   

108            try {

109                System.out.println("Creatingconnection.");

110                conn = dataSource.getConnection();

111                System.out.println("Creatingstatement.");

112                stmt = conn.createStatement();

113                System.out.println("Executingstatement.");

114                rset = stmt.executeQuery(args[1]);

115               System.out.println("Results:");

116                int numcols =rset.getMetaData().getColumnCount();

117                while(rset.next()) {

118                    for(int i=1;i<=numcols;i++) {

119                       System.out.print("\t" + rset.getString(i));

120                    }

121                   System.out.println("");

122                }

123            } catch(SQLException e) {

124                e.printStackTrace();

125            } finally {

126                try { if (rset != null)rset.close(); } catch(Exception e) { }

127                try { if (stmt != null)stmt.close(); } catch(Exception e) { }

128                try { if (conn != null)conn.close(); } catch(Exception e) { }

129            }

130        }

131   

132        public static DataSourcesetupDataSource(String connectURI) {

133            //

134            // First, we'll create aConnectionFactory that the

135            // pool will use to create Connections.

136            // We'll use theDriverManagerConnectionFactory,

137            // using the connect string passed inthe command line

138            // arguments.

139            //

140            ConnectionFactory connectionFactory =

141                newDriverManagerConnectionFactory(connectURI,null);

142   

143            //

144            // Next we'll create thePoolableConnectionFactory, which wraps

145            // the "real" Connectionscreated by the ConnectionFactory with

146            // the classes that implement thepooling functionality.

147            //

148            PoolableConnectionFactorypoolableConnectionFactory =

149                newPoolableConnectionFactory(connectionFactory);

150   

151            //

152            // Now we'll need a ObjectPool thatserves as the

153            // actual pool of connections.

154            //

155            // We'll use a GenericObjectPoolinstance, although

156            // any ObjectPool implementation willsuffice.

157            //

158            ObjectPool connectionPool =

159                new GenericObjectPool(poolableConnectionFactory);

160   

161            //

162            // Finally, we create the PoolingDriveritself,

163            // passing in the object pool wecreated.

164            //

165            PoolingDataSource dataSource = newPoolingDataSource(connectionPool);

166   

167            return dataSource;

168        }

169    }

你可能感兴趣的:(Open,Source)