數據庫連接池
1、數據庫連接池的基本介紹
數據庫連接池是一種用于管理數據庫連接的機制。數據庫連接的建立是一個相對耗時的操作,包括網絡通信、身份驗證等過程。連接池預先創建一定數量的數據庫連接,并對這些連接進行管理。當應用程序需要訪問數據庫時,它可以從連接池中獲取一個連接,使用完畢后再將連接歸還給連接池,而不是每次都重新建立和銷毀連接,這樣可以提高數據庫訪問的效率。數據庫連接池會釋放空閑時間超過最大空閑時間的連接,避免數據庫連接被遺漏。
連接池基本的思想是在系統初始化的時候,將數據庫連接作為對象存儲在內存中,當用戶需要訪問數據庫時,并非建立一個新的連接,而是從連接池中取出一個已建立的空閑連接對象。使用完畢后,用戶也并非將連接關閉,而是將連接放回連接池中,以供下一個請求訪問使用。而連接的建立、斷開都由連接池自身來管理。同時,還可以通過設置連接池的參數來控制連接池中的初始連接數、連接的上下限數以及每個連接的最大使用次數、最大空閑時間等等。也可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等。
1.1、為什么要使用連接池
一個數據庫連接對象均對應一個物理數據庫連接,每次操作都打開一個物理連接,使用完都關閉連接,這樣造成系統的性能低下。使用數據庫連接池可以節約資源,程序更加高效。

數據庫連接池的解決方案是在應用程序啟動時建立足夠的數據庫連接,并講這些連接組成一個連接池(簡單說:在一個“池”里放了好多半成品的數據庫連接對象),由應用程序動態地對池中的連接進行申請、使用和釋放。對于多于連接池中連接數的并發請求,應該在請求隊列中排隊等待。并且應用程序可以根據池中連接的使用率,動態增加或減少池中的連接數。連接池技術盡可能多地重用了消耗內存地資源,大大節省了內存,提高了服務器地服務效率,能夠支持更多的客戶服務。通過使用連接池,將大大提高程序運行效率,同時,我們可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等。
1.2、Java中常見的數據連接池
在Java中開源的數據庫連接池有以下幾種 :
- C3P0:是一個開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發布,包括了實現jdbc3和jdbc2擴展規范說明的Connection 和Statement 池的DataSources 對象。
- Druid:Druid不僅是一個數據庫連接池,還包含一個ProxyDriver、一系列內置的JDBC組件庫、一個SQL Parser。支持所有JDBC兼容的數據庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等。
- Proxool:是一個Java SQL Driver驅動程序,提供了對選擇的其它類型的驅動程序的連接池封裝。可以非常簡單的移植到現存的代碼中,完全可配置,快速、成熟、健壯。可以透明地為現存的JDBC驅動程序增加連接池功能。
- DBCP:DBCP是一個依賴Jakarta commons-pool對象池機制的數據庫連接池,DBCP可以直接的在應用程序中使用,Tomcat的數據源使用的就是DBCP。
我們在使用連接池時,連接池給我們提供了獲取數據庫連接的方法,但使用完成后還是要調用 close 方法來關閉連接。但是實際上使用連接池時調用 close() 方法實際上并沒有關閉連接,只是將該連接歸還給連接池。
1.3、應用連接池和數據庫連接池的關系
應用的連接池其實指的就是跟它的后端(數據庫、MQ)服務的連接池,沒有單獨說應用連接池的概念
而應用與前端頁面之間一般沒有連接池概念。在典型的 Web 應用架構中,應用服務器主要負責處理業務邏輯和與后端資源(如數據庫)的交互,前端頁面(如 HTML、CSS、JavaScript 構成的網頁)通過 HTTP 協議與應用服務器通信。這個通信過程是基于請求 - 響應模式,沒有像后端數據庫連接那樣的連接池機制。例如,當用戶在瀏覽器中訪問一個網頁時,瀏覽器發送 HTTP 請求到應用服務器,應用服務器處理請求并返回響應(如一個包含動態數據的 HTML 頁面),這個過程每次請求完成后連接就會關閉,不存在長期保持的連接用于重復利用,所以不需要連接池。
2、C3P0連接池
C3P0是一個開源的JDBC連接池,它實現了數據源與JNDI綁定,支持JDBC3規范和實現了JDBC2的標準擴展說明的Connection和Statement池的DataSources對象。即將用于連接數據庫的連接整合在一起形成一個隨取隨用的數據庫連接池(Connection pool)。
2.1、基于maven項目使用C3P0
首先添加依賴:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.12</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
因為使用的是 mysql,所以我們要添加 mysql 的驅動包依賴。
如果新建的 maven 項目是 java SE 的項目,我們需要手動建一個 resource 包,并且標記為 resource folder,因為我們需要將 cp30 的配置文件放在該包下。
新建 cp30 的配置文件 c3p0-config.xml,并將該配置文件放在 resource 包下。只有放在 resource 包下,編譯過后你才會發現 cp30 的配置文件生成在 target 的classes 文件夾下。最終目錄如下:

c3p0-config.xml 文件內容如下:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!--使用默認的配置讀取連接池對象--> <default-config> <!--連接參數--> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/db_test</property> <property name="user">root</property> <property name="password">123456</property> <!--連接池參數--> <!--初始化申請的連接數量。在初始化時就申請的連接數--> <property name="initialPoolSize">5</property> <!--超時時間--> <property name="checkoutTimeout">3000</property> <property name="maxIdleTime">30</property> <!--最大的連接數量。超過該數目不會再申請多的連接--> <property name="maxPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxStatements">200</property> </default-config> <!--配置連接池mysql--> <named-config name="mysql"> <property name="acquireIncrement">50</property> <property name="initialPoolSize">100</property> <property name="minPoolSize">50</property> <property name="maxPoolSize">1000</property><!-- intergalactoApp adopts a different approach to configuring statement caching --> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> </named-config> <!--配置連接池2--> <!--......--> </c3p0-config>
在配置文件中可以配置多個連接池,default-config 是默認的連接池配置,可以通過 name-config 標簽來配置其他的連接池配置,通過 name 屬性為該配置指定名稱。在使用額外的連接池配置時,最終的配置信息是默認配置和該額外的配置的并集,并且以額外的配置優先。
在獲取連接池對象時,如果不傳參則使用的是默認配置,也可以通過使用 name 參數來使用指定配置。代碼如下,我們測試一下連接池是否配置成功:
public class CP30_test { public void test01() throws SQLException { //1.創建數據庫連接池對象。不指定名稱使用的是默認配置,即default-config DataSource ds = new ComboPooledDataSource();
//可以通過參數來使用指定的配置 //DataSource ds = new ComboPooledDataSource("mysql-config"); //2.獲取連接對象 Connection conn = ds.getConnection(); //3.打印 System.out.println(conn); } }
打印結果如下:

說明配置已經成功。我們可以通過該連接池來獲取連接對象,JDBC 的其他步驟跟不使用連接池時一樣。
2.1.1、新建C3P0Util工具類
一般在使用數據庫連接池時,我們會新建一個工具類來方便我們使用連接池。
工具類代碼示例:
package com.c3p0.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Util { //使用ComboPooledDataSource來生成DataSource的實例 private static DataSource dataSource =new ComboPooledDataSource(); //從連接池中獲取連接 public static Connection getConnection() { try { return dataSource.getConnection(); }catch (SQLException e) { // TODO Auto-generated catch block throw new RuntimeException(); } } //釋放連接回連接池 public static void release(Connection conn, Statement stmt, ResultSet rs) { if (rs !=null) { try { rs.close(); }catch (Exception e) { e.printStackTrace(); } rs =null; } if (stmt !=null) { try { stmt.close(); }catch (Exception e) { e.printStackTrace(); } stmt =null; } if (conn !=null) { try { conn.close(); }catch (Exception e) { e.printStackTrace(); } conn =null; } } }
使用工具類:
public class TestCRUD { public void testInsert() { Connection conn =null; PreparedStatement ps =null; conn = C3P0Util.getConnection(); try { ps = conn.prepareStatement("INSERT INTO users (username,PASSWORD,email,birthday)VALUES('SUN99','123','123456@qq.com','2020-01-01')"); ps.executeUpdate(); System.out.println("添加操作執行成功!"); }catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("添加操作執行失敗!"); }finally { C3P0Util.release(conn, ps,null); } } }
2.2、c3p0配置參數詳解
常見配置參數:
-
initialPoolSize:初始化連接數。在容器初始化時就申請的連接數量
-
maxPoolSize:最大連接數。如果需要的連接超過該數目容器也不會再申請多的連接,此時數據庫連接可能就會超時,無法查詢到數據。最大連接數應該根據服務器的性能來靈活配置
<c3p0-config> <default-config> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default:3 --> <property name="acquireIncrement">3</property> <!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default:30 --> <property name="acquireRetryAttempts">30</property> <!--兩次連接中間隔時間,單位毫秒。Default:1000 --> <property name="acquireRetryDelay">1000</property> <!--連接關閉時默認將所有未提交的操作回滾。Default:false --> <property name="autoCommitOnClose">false</property> <!--c3p0將建一張名為Test的空表,并使用其自帶的查詢語句進行測試。如果定義了這個參數那么 屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試 使用。Default:null--> <property name="automaticTestTable">Test</property> <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 保留,并在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試 獲取連接失敗后該數據源將申明已斷開并永久關閉。Default:false--> <property name="breakAfterAcquireFailure">false</property> <!--當連接池用完時客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出 SQLException,如設為0則無限期等待。單位毫秒。Default:0 --> <property name="checkoutTimeout">100</property> <!--通過實現ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。 Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester--> <property name="connectionTesterClassName"></property> <!--指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那么無需設置,默認null即可 Default:null--> <property name="factoryClassLocation">null</property> <!--強烈不建議使用該方法,將這個設置為true可能會導致一些微妙而奇怪的bug--> <property name="forceIgnoreUnresolvedTransactions">false</property> <!--每60秒檢查所有連接池中的空閑連接。Default:0 --> <property name="idleConnectionTestPeriod">60</property> <!--初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default:3 --> <property name="initialPoolSize">3</property> <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default:0 --> <property name="maxIdleTime">60</property> <!--連接池中保留的最大連接數。Default:15 --> <property name="maxPoolSize">15</property> <!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由于預緩存的statements 屬于單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。 如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default:0--> <property name="maxStatements">100</property> <!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。Default:0 --> <property name="maxStatementsPerConnection"></property> <!--c3p0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能 通過多線程實現多個操作同時被執行。Default:3--> <property name="numHelperThreads">3</property> <!--當用戶調用getConnection()時使root用戶成為去獲取連接的用戶。主要用于連接池連接非c3p0 的數據源時。Default:null--> <property name="overrideDefaultUser">root</property> <!--與overrideDefaultUser參數對應使用的一個參數。Default:null--> <property name="overrideDefaultPassword">password</property> <!--密碼。Default:null--> <property name="password"></property> <!--定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意: 測試的表必須在初始數據源的時候就存在。Default:null--> <property name="preferredTestQuery">select id from test where id=1</property> <!--用戶修改系統配置參數執行前最多等待300秒。Default:300 --> <property name="propertyCycle">300</property> <!--因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable 等方法來提升連接測試的性能。Default:false --> <property name="testConnectionOnCheckout">false</property> <!--如果設為true那么在取得連接的同時將校驗連接的有效性。Default:false --> <property name="testConnectionOnCheckin">true</property> <!--用戶名。Default:null--> <property name="user">root</property> <!--早期的c3p0版本對JDBC接口采用動態反射代理。在早期版本用途廣泛的情況下這個參數 允許用戶恢復到動態反射代理以解決不穩定的故障。最新的非反射代理更快并且已經開始 廣泛的被使用,所以這個參數未必有用。現在原先的動態反射與新的非反射代理同時受到 支持,但今后可能的版本可能不支持動態反射代理。Default:false--> <property name="usesTraditionalReflectiveProxies">false</property> </default-config> </c3p0-config>
3、druid連接池
3.1、druid連接池的基本介紹
Druid是阿里開源的數據庫連接池,作為后起之秀,性能比dbcp、c3p0更高,使用也越來越廣泛。Druid不僅是一個數據庫連接池,還包含一個ProxyDriver、一系列內置的JDBC組件庫、一個SQL Parser。支持所有JDBC兼容的數據庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等。
druid的優點:
- 高性能。性能比dbcp、c3p0高很多。
- 只要是jdbc支持的數據庫,druid都支持,對數據庫的支持性好。并且Druid針對oracle、mysql做了特別優化。
- 提供監控功能。可以監控sql語句的執行時間、ResultSet持有時間、返回行數、更新行數、錯誤次數、錯誤堆棧等信息,來了解連接池、sql語句的工作情況,方便統計、分析SQL的執行性能
3.2、基于maven項目使用druid連接池
首先添加依賴,因為使用的是 mysql,所以也要添加 mysql 的驅動依賴:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.8</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.12</version> </dependency>
依賴添加完成,我們需要添加 druid 的配置文件。如果新建的 maven 項目是 java SE 的項目,我們需要手動建一個 resource 包,并且該文件應該被標記為 resource folder,因為我們需要將 cp30 的配置文件放在該包下。
新建 druid 的配置文件 druid.properties(可自定義文件名稱),并將該配置文件放在 resource 包下(只有放在 resource 包下,編譯過后你才會發現 cp30 的配置文件生成在 target 的classes 文件夾下)。最終目錄如下:

druid.properties 配置文件的內容如下:
url=jdbc:mysql://localhost:3306/test #這個可以缺省的,會根據url自動識別 driverClassName=com.mysql.jdbc.Driver username=root password=123456 ##初始連接數,默認0 initialSize=10 #最大連接數,默認8 maxActive=30 #最小閑置數 minIdle=10 #獲取連接的最大等待時間,單位毫秒 maxWait=2000 #緩存PreparedStatement,默認false poolPreparedStatements=true #緩存PreparedStatement的最大數量,默認-1(不緩存)。大于0時會自動開啟緩存PreparedStatement,所以可以省略上一句設置 maxOpenPreparedStatements=20
此時我們就可以通過連接池來獲取數據庫連接了:
package org.example; import com.alibaba.druid.pool.DruidDataSourceFactory; import org.junit.Test; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties; public class DruidTest { @Test public void test01() { Connection connection = null; PreparedStatement preparedStatement = null; try { //數據源配置 Properties properties=new Properties(); //通過當前類的class對象獲取資源文件 InputStream is = DruidTest.class.getResourceAsStream("/druid.properties"); properties.load(is); //返回的是DataSource,不是DruidDataSource DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); //獲取連接 connection = dataSource.getConnection(); //PreparedStatement接口 String sql = "update user set name = 'newName' where id = 2"; preparedStatement = connection.prepareStatement(sql); preparedStatement.execute(); } catch (Exception e) { e.printStackTrace(); } finally { if(preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } //關閉連接。實際上是將連接歸還給連接池 try { if(connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }
3.2.1、建立druid工具類
一般在使用數據庫連接池時,我們會新建一個工具類來方便我們使用連接池。下面我們建立一個 druid 工具類,在該類中實現多個方法封裝以我們方便使用,包括:獲取數據庫連接池、獲取數據庫的連接、關閉數據庫連接資源。
代碼如下:
package org.example; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; 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; public class DruidUtil { private static DataSource ds; static { //加載配置文件和建立連接池 try { Properties pro = new Properties(); InputStream resourceAsStream = DruidUtil.class.getClassLoader().getResourceAsStream("Druid.properties"); pro.load(resourceAsStream); ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 獲取數據庫連接池 * @return */ public static DataSource getDataSource(){ return ds; } /** * 獲取連接池中的一個連接 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } /** * 關閉數據庫的資源 三個對象都存在時 * @param conn * @param res * @param pstmt */ public static void close(Connection conn, ResultSet res, PreparedStatement pstmt){ if (conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if (res!=null){ try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pstmt!=null){ try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 關閉數據庫的連接(只存在Connection和PreparedStatement對象時) * @param conn * @param pstmt */ public void close(Connection conn,PreparedStatement pstmt){ if (conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pstmt!=null){ try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
我們就可以使用 druid 工具類來獲取數據庫連接、關閉資源。
實例代碼如下:
public class DruidTest { public void utilTest() { Connection connection = null; ResultSet resultSet = null; PreparedStatement preparedStatement = null; try { connection = DruidUtil.getConnection(); //獲取數據庫連接 String sql = "select * from user"; preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ Integer id = resultSet.getInt("id"); String name = resultSet.getString("name"); String password = resultSet.getString("password"); System.out.println(id+" "+name+" "+password); } } catch (SQLException e) { e.printStackTrace(); } finally { DruidUtil.close(connection,resultSet,preparedStatement); //釋放資源 } } }

浙公網安備 33010602011771號