JDBC數據庫連接池的必要性
一、在使用開發基於數據庫的web程序時,傳統的模式基本是按一下步驟:
1)在主程序(如servlet/beans)中建立數據庫連接
2)進行sql操作
3)斷開數據庫連接
二、這種模式開發,存在的問題:
1)普通的JDBC數據庫連接使用DriverManager來獲取,每次向數據庫建立連接的時候都要將Connection加載進內存中,再驗證用戶名和密碼(得花費0.05s~1s的時間).需要數據庫連接的時候,就向數據庫要求一個,執行完成后就斷開連接。這樣的方式將消耗大連的時間和資源。數據庫的連接資源並沒有得到很好的重復利用。若同時有幾百人甚至幾千人同時在線,頻繁的進行數據庫連接將占用很多的系統資源,嚴重的甚至會造成服務器的崩潰。
2)對於每一次數據庫連接,使用完成后都要斷開。否則,如果程序出現異常而未能關閉,將導致數據庫系統的內存泄露,並最終導致重啟數據庫。
3)這種開發不能控制被創建的連接對象數,系統資源被毫無顧忌的分配出去,如連接過多,也可能導致內存泄露,服務器崩潰
數據庫連接池(connection pool)
1)為了解決傳統開發中的數據庫連接問題,可以采用數據庫連接池技術。
2)數據庫連接池的基本思想就是為數據庫連接建立一個“緩沖池”,預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,只需要從“緩沖池”中取出一個,使用完畢之后再放回去。
3)數據庫連接池負責分配、管理和釋放數據庫連接,他允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個。
4)數據庫連接池在初始化的時候將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量由最小數據庫連接數來設定的。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這么多的連接數量。
連接池的最大數據庫連接數量限定了這個連接池能占有的最大連接數,當應用程序想連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待序列中.
數據庫連接池(connection pool)的工作原理
數據庫連接池(connection pool)的優點
1) 資源重用
由於數據庫連接得到重用,避免了頻繁創建、釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增進了系統運行環境的平穩性(減少內存碎片以及數據庫臨時進程/線程的數量);
2) 更快的系統響應速度
數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置於池中備用。此時連接的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間;
3) 新的資源分配手段
對於多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接的配置,實現數據庫連接池技術,幾年前也許還是個新鮮話題,對於目前的業務系統而言,如果設計中還沒有考慮到連接池的應用,那么…….快在設計文檔中加上這部分的內容吧。某一應用最大可用數據庫連接數的限制,避免某一應用獨占所有數據庫資源;
4) 統一的連接管理,避免數據庫連接泄漏
在較為完備的數據庫連接池實現中,可根據預先的連接占用超時設定,強制收回被占用連接。從而避免了常規數據庫連接操作中可能出現的資源泄漏。一個最小化的數據庫連接池實現;
* 1.加入JAR包(2個)
* commons-dbcp2-2.1.1.jar
* Commons-pool.jar
* 2.創建數據庫連接池
* 3.為數據源實例指定必須的屬性
* 4.從數據源中獲取數據庫連接
具體代碼實現:
1 @Test 2 public void testDBCP() throws Exception{ 3 DataSource dataSource=null; 4 //1.創建DBCP數據源實例 5 dataSource=new BasicDataSource(); 6 //2.為數據源實例指定必須的屬性 7 ((BasicDataSource) dataSource).setUsername("root"); 8 ((BasicDataSource) dataSource).setPassword("123456"); 9 ((BasicDataSource) dataSource).setUrl("jdbc:mysql://localhost:3306/atguigu"); 10 ((BasicDataSource) dataSource).setDriverClassName("com.mysql.jdbc.Driver"); 11 //3.指定數據源一些可選的屬性(可以看下載的Jar包中的index.html)API文檔 12 /* 13 * maxIdle,最大空閑數,數據庫連接的最大空閑時間。超過空閑時間,數據庫連接將被標記為不可用,然后被釋放。設為0表示無限制。 14 MaxActive,連接池的最大數據庫連接數。設為0表示無限制。 15 maxWait ,最大建立連接等待時間。如果超過此時間將接到異常。設為-1表示無限制 16 【溫馨提示】:pool2中修改如下: 17 maxActive ==> maxTotal 18 maxWait ==> maxWaitMillis 19 */ 20 //1).指定數據庫連接池中初始化連接的個數 21 ((BasicDataSource) dataSource).setInitialSize(10); 22 //2).指定數據庫連接池中最大連接的個數:同一時刻可以同時向數據庫申請的數據庫連接 23 ((BasicDataSource) dataSource).setMaxTotal(5); 24 //3).指定數據庫連接池中最小連接的個數:在連接池中保存的最小空閑連接的數量 25 ((BasicDataSource) dataSource).setMinIdle(5); 26 //4).指定數據庫連接池分配連接的最長時間,單位為毫秒,超出改時間將拋出異常 27 ((BasicDataSource) dataSource).setMaxWaitMillis(1000*5); 28 //4.從數據源中獲取數據庫連接 29 30 Connection connection=dataSource.getConnection(); 31 System.out.println(connection.getClass()); 32 33 }
方式二:
使用DBCP數據庫連接池的步驟
* 1.加載dbcp的properties配置文件
* 配置文件中的鍵值對需要來自BasicDataSource這個類的屬性
* 2.調用BasicDataSourceFactory的createDataSource方法
* 創建DataSource實例
* 3.從DataSource中獲取數據庫連接
dbcp.properties文件內容如下:
driver=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql://localhost:3306/atguigu user=root password=123456 initialSize=10 maxTotal=50 minIdle=5 maxWaitMillis=5000
具體代碼實現:
1 @Test 2 public void testDBCPWithDataSourceFactory() throws Exception{ 3 Properties properties=new Properties(); 4 InputStream inputStream=JDBCTest.class.getClassLoader() 5 .getResourceAsStream("dbcp.properties"); 6 properties.load(inputStream); 7 DataSource dataSource= 8 BasicDataSourceFactory.createDataSource(properties); 9 System.out.println(dataSource.getConnection()); 10 11 BasicDataSource basicDataSource= 12 (BasicDataSource) dataSource; 13 System.out.println(basicDataSource.getMaxTotal()); 14 15 }
C3P0數據庫連接池
方式一:
這里我們需要加入兩個JAR包:c3p0-0.9.5.2.jar和mchange-commons-java-0.2.11.jar
我們下載的c3p0JAR包中的doc文件夾中的index.html文件,點擊該文件->Contents->Quickstart,我們可以看到c3p0數據庫連接池的創建步驟,如下圖所示(我們使用一些JAR包的時候要善於利用好幫助文檔):
import com.mchange.v2.c3p0.*; ... ComboPooledDataSource cpds = new ComboPooledDataSource(); cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" ); cpds.setUser("dbuser"); cpds.setPassword("dbpassword");
於是我們的代碼就可以這樣寫了:
1 @Test 2 public void testC3P0() throws Exception{ 3 ComboPooledDataSource cpds = new ComboPooledDataSource(); 4 cpds.setDriverClass( "com.mysql.jdbc.Driver" ); //loads the jdbc driver 5 cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/atguigu" ); 6 cpds.setUser("root"); 7 cpds.setPassword("123456"); 8 System.out.println(cpds.getConnection()); 9 }
運行結果:證明創建成功
五月 09, 2016 4:35:56 下午 com.mchange.v2.log.MLog 信息: MLog clients using java 1.4+ standard logging. 五月 09, 2016 4:35:57 下午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10] 五月 09, 2016 4:35:57 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hge1d39g158wpld8tx485|1588809, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge1d39g158wpld8tx485|1588809, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/atguigu, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ] com.mchange.v2.c3p0.impl.NewProxyConnection@6776ad [wrapping: com.mysql.jdbc.JDBC4Connection@108b2d7]
方式二:使用配置文件的形式
具體的步驟:
* 1.創建c3p0-config.xml文件,參考幫助文檔中 * Appendix B: Configuation Files, etc.的內容 * 2.創建ComboPooledDataSource實例: * DataSource dataSource = new ComboPooledDataSource("helloc3p0"); *3.從DataSource實例中獲取數據庫連接
其中c3p0-config.xml中的內容我們進行更改成下面的形式:每一行代表什么我都給出了詳細的解釋
<c3p0-config> <named-config name="helloc3p0"> <!-- 指定數據源的基本屬性 --> <property name="user">root</property> <property name="password">123456</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/atguigu</property> <!-- 若數據庫中連接數不足時,一次向數據庫服務器申請多少個連接 --> <property name="acquireIncrement">5</property> <!-- 初始化數據庫連接時連接的數量 --> <property name="initialPoolSize">10</property> <!-- 數據庫連接池中最小的數據庫連接數 --> <property name="minPoolSize">5</property> <!-- 數據庫連接池中最大的數據庫連接數 --> <property name="maxPoolSize">10</property> <!-- 數據庫連接池可以維護的Statement的個數 --> <property name="maxStatements">10</property> <!-- 每個連接同時可以使用的Statement對象的個數 --> <property name="maxStatementsPerConnection">5</property> </named-config> </c3p0-config>
我們可以看到named-config name="helloc3p0",我們要創建的數據庫連接池的名稱是helloc3p0;
第二種方式的具體代碼實現:
1 @Test 2 public void testC3P0withConfigFile() throws Exception{ 3 DataSource dataSource = 4 new ComboPooledDataSource("helloc3p0"); 5 System.out.println(dataSource.getConnection()); 6 ComboPooledDataSource comboPooledDataSource= 7 (ComboPooledDataSource) dataSource; 8 System.out.println(comboPooledDataSource.getMaxStatements()); 9 }
測試一些,運行結果:
1 五月 09, 2016 4:42:15 下午 com.mchange.v2.log.MLog 2 信息: MLog clients using java 1.4+ standard logging. 3 五月 09, 2016 4:42:15 下午 com.mchange.v2.c3p0.C3P0Registry 4 信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10] 5 五月 09, 2016 4:42:15 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 6 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 5, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> helloc3p0, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge1d39g1594tqaufcxlm|1588809, idleConnectionTestPeriod -> 0, initialPoolSize -> 10, jdbcUrl -> jdbc:mysql://localhost:3306/atguigu, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 10, maxStatementsPerConnection -> 5, minPoolSize -> 5, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ] 7 com.mchange.v2.c3p0.impl.NewProxyConnection@1bcffb5 [wrapping: com.mysql.jdbc.JDBC4Connection@c1f859] 8 10
此時我們JDBCtools中的獲取連接的函數getConnection就可以改寫代碼了,不是每一次都創建新的數據庫連接,而是從數據庫連接池中獲取連接,release函數也不是真正的關閉連接了,而是把數據庫連接繼續放到連接池中。
1 private static DataSource dataSource=null; 2 //數據庫連接池只被初始化一次 3 static { 4 dataSource=new ComboPooledDataSource("helloc3p0"); 5 } 6 // 獲取數據庫連接 7 public static Connection getConnection() throws Exception{ 8 9 /* ClassNotFoundException, SQLException { 10 Properties properties = new Properties(); 11 InputStream inputStream = JDBCTest.class.getClassLoader() 12 .getResourceAsStream("jdbc.properties"); 13 properties.load(inputStream); 14 String user = properties.getProperty("user"); 15 String password = properties.getProperty("password"); 16 String jdbcUrl = properties.getProperty("jdbcUrl"); 17 String driverClass = properties.getProperty("driver"); 18 Class.forName(driverClass); 19 Connection connection = DriverManager.getConnection(jdbcUrl, user, 20 password);*/ 21 22 return dataSource.getConnection(); 23 }
數據庫連接池一般只有一個,所以我們用static修飾,並使用靜態代碼塊的形式初始化數據庫連接池,getConnection中的被注釋代碼使我們原來的獲取連接的方式,可以看到代碼簡潔了不少!
最后附上兩部分用到的JAR包:鏈接:http://pan.baidu.com/s/1eSjUDn8 密碼:49zw
本文為博主原創文章,轉載請注明出處:http://www.cnblogs.com/ysw-go/
1、本博客的原創原創文章,都是本人平時學習所做的筆記,如有錯誤,歡迎指正。
2、如有侵犯您的知識產權和版權問題,請通知本人,本人會即時做出處理文章。
3、本博客的目的是知識交流所用,轉載自其它博客或網站,作為自己的參考資料的,感謝這些文章的原創人員