一、數據庫連接池的概念
用池來管理Connection,這可以重復使用Connection。有了池,所以我們就不用自己來創建Connection,而是通過池來獲取Connection對象。當使用完Connection后,調用Connection的close()方法也不會真的關閉Connection,而是把Connection“歸還”給池。池就可以再利用這個Connection對象了。
JDBC數據庫連接池接口(DataSource)
Java為數據庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商可以讓自己的連接池實現這個接口。這樣應用程序可以方便的切換不同廠商的連接池!
開源數據庫連接池
現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之為數據源,數據源中都包含了數據庫連接池的實現。
也有一些開源組織提供了數據源的獨立實現:
- DBCP 數據庫連接池
- C3P0 數據庫連接池
在使用了數據庫連接池之后,在項目的實際開發中就不需要編寫連接數據庫的代碼了,直接從數據源獲得數據庫的連接。
JDBC數據庫連接池接口(DataSource)
Java為數據庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商可以讓自己的連接池實現這個接口。這樣應用程序可以方便的切換不同廠商的連接池!
二、DBCP
DBCP是Apache提供的一款開源免費的數據庫連接池!Tomcat 的連接池正是采用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。Hibernate3.0之后不再對DBCP提供支持!因為Hibernate聲明DBCP有致命的缺欠!
需要應用程序應在系統中增加如下兩個 jar 文件:
- Commons-dbcp.jar:連接池的實現
- Commons-pool.jar:連接池實現的依賴庫
實現DBCP
1. 加入jar包
2.編寫dbcpconfig.properties
#連接設置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/testdb username=root password=123456 #<!-- 初始化連接 --> initialSize=10 #最大連接數量 maxActive=50 #<!-- 最大空閑連接 --> maxIdle=20 #<!-- 最小空閑連接 --> minIdle=5 #<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等於60秒 --> maxWait=60000 #JDBC驅動建立連接時附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;] #注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這里不需要包含他們。 connectionProperties=useUnicode=true;characterEncoding=UTF8 #指定由連接池所創建的連接的自動提交(auto-commit)狀態。 defaultAutoCommit=true #driver default 指定由連接池所創建的連接的只讀(read-only)狀態。 #如果沒有設置該值,則“setReadOnly”方法將不被調用。(某些驅動並不支持只讀模式,如:Informix) defaultReadOnly=false #driver default 指定由連接池所創建的連接的事務級別(TransactionIsolation)。 #可用值為下列之一:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=REPEATABLE_READ
如下圖所示:
3、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中創建池
package cn.zy.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; /* * 數據庫連接工具類DBCP */ public class JdbcUtils_DBCP { /* * 在java中,編寫數據庫連接池需實現java.sql.DataSource接口,每一種數據庫連接池都是DataSource接口的實現 * DBCP連接池就是javax.sql.DataSource接口的一個具體實現 */ private static DataSource ds =null; //在靜態代碼塊中創建數據庫連接池 static { try { //加載配置文件 InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); Properties prop = new Properties(); prop.load(in); //創建數據源 ds = BasicDataSourceFactory.createDataSource(prop); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /* * 從數據源中獲取數據庫連接 */ 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 (Exception e){ e.printStackTrace(); } if(st!=null){ try { st.close(); } catch (Exception e) { e.printStackTrace(); } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } }
測試連接
package cn.zy.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import cn.zy.utils.JdbcUtils_DBCP; /* * 測試DBCP數據源 */ public class DbcpTest { @Test public void dbcpDataSourceTest(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { //獲取數據庫連接 conn = JdbcUtils_DBCP.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "D"); st.setFloat(2, 2000); st.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally{ //釋放資源 JdbcUtils_DBCP.release(conn, st, rs); } } }
三、C3P0數據源
C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規范和JDBC2的標准擴展。目前使用它的開源項目有Hibernate,Spring等。C3P0數據源在項目開發中使用得比較多。
- dbcp沒有自動回收空閑連接的功能
- c3p0有自動回收空閑連接功能
在應用程序中加入C3P0連接池
1.導入相關jar包
c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle數據庫,那么還需要導入c3p0-oracle-thin-extras-0.9.2-pre1.jar
2、在src下加入C3P0的配置文件:c3p0-config.xml,注意:
配置文件要求:
l 文件名稱:必須叫c3p0-config.xml
l 文件位置:必須在src下
c3p0-config.xml的配置信息如下:
<?xml version="1.0" encoding="UTF-8"?> <!-- c3p0-config.xml必須位於類路徑下面 private static ComboPooledDataSource ds; static{ try { ds = new ComboPooledDataSource("MySQL"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } --> <c3p0-config> <!-- C3P0的缺省(默認)配置, 如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource();”這樣寫就表示使用的是C3P0的缺省(默認)配置信息來創建數據源 --> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> <!-- C3P0的命名配置, 如果在代碼中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”這樣寫就表示使用的是name是MySQL的配置信息來創建數據源 --> <named-config name="MySQL"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </named-config> </c3p0-config>
如圖:
3、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中創建池
1 package cn.zy.utils; 2 3 import java.sql.Connection; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 import javax.sql.DataSource; 9 10 11 import com.mchange.v2.c3p0.ComboPooledDataSource; 12 13 /* 14 * 數據庫連接工具類 15 */ 16 public class JdbcUtils_C3P0 { 17 private static ComboPooledDataSource dataSource = null; 18 //在靜態塊中創建數據庫連接池 19 static{ 20 try { 21 //通過代碼創建數據庫連接池 22 /*ds = new ComboPooledDataSource(); 23 ds.setDriverClass("com.mysql.jdbc.Driver"); 24 ds.setJdbcUrl("jdbc:mysql://localhost:3306/testdb"); 25 ds.setUser("root"); 26 ds.setPassword("123456"); 27 ds.setInitialPoolSize(10); 28 ds.setMinPoolSize(5); 29 ds.setMinPoolSize(20);*/ 30 31 //通過讀取xml來獲取數據源 32 //ds = new ComboPooledDataSource();//使用C3P0的默認配置來創建數據源 33 dataSource = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置來創建數據源 34 } catch (Exception e) { 35 throw new ExceptionInInitializerError(e); 36 } 37 } 38 39 /* 40 * 從數據源中獲取數據庫連接 41 */ 42 public static Connection getConnection() throws SQLException { 43 return dataSource.getConnection(); 44 } 45 46 /* 47 * 返回連接池 48 */ 49 public static DataSource getDataSource() { 50 return dataSource; 51 } 52 /* 53 * 釋放資源 54 */ 55 public static void release(Connection conn,Statement st,ResultSet rs){ 56 if(rs!=null){ 57 try{ 58 //關閉存儲查詢結果的ResultSet對象 59 rs.close(); 60 }catch (Exception e) { 61 e.printStackTrace(); 62 } 63 rs = null; 64 } 65 if(st!=null){ 66 try{ 67 //關閉負責執行SQL命令的Statement對象 68 st.close(); 69 }catch (Exception e) { 70 e.printStackTrace(); 71 } 72 } 73 74 if(conn!=null){ 75 try{ 76 //將Connection連接對象還給數據庫連接池 77 conn.close(); 78 }catch (Exception e) { 79 e.printStackTrace(); 80 } 81 } 82 } 83 } 84
編寫測試類:
package cn.zy.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import cn.zy.utils.JdbcUtils_C3P0; public class C3p0Test { @Test public void fun(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils_C3P0.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "F"); st.setFloat(2,3000); st.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } } }
四、配置Tomcat數據源
在實際開發中,我們有時候還會使用服務器提供給我們的數據庫連接池,比如我們希望Tomcat服務器在啟動的時候可以幫我們創建一個數據庫連接池,那么我們在應用程序中就不需要手動去創建數據庫連接池,直接使用Tomcat服務器創建好的數據庫連接池即可。要想讓Tomcat服務器在啟動的時候幫我們創建一個數據庫連接池,那么需要簡單配置一下Tomcat服務器。
4.1 Tomcat配置JNDI資源
JNDI(Java Naming and Directory Interface),Java命名和目錄接口,它對應於J2SE中的javax.naming包,
這 套API的主要作用在於:它可以把Java對象放在一個容器中(JNDI容器),並為容器中的java對象取一個名稱,以后程序想獲得Java對象,只需 通過名稱檢索即可。其核心API為Context,它代表JNDI容器,其lookup方法為檢索容器中對應名稱的對象。
Tomcat服務器創建的數據源是以JNDI資源的形式發布的,所以說在Tomat服務器中配置一個數據源實際上就是在配置一個JNDI資源,通過查看Tomcat文檔,我們知道使用如下的方式配置tomcat服務器的數據源:
<Context> <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" username="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/testdb" maxActive="8" maxIdle="4"/> </Context>
服務器創建好數據源之后,我們的應用程序又該怎么樣得到這個數據源呢,Tomcat服務器創建好數據源之后是以JNDI的形式綁定到一個JNDI容器中的,我們可以把JNDI想象成一個大大的容器,我們可以往這個容器中存放一些對象,一些資源,JNDI容器中存放的對象和資源都會有一個獨一無二的名稱,應用程序想從JNDI容器中獲取資源時,只需要告訴JNDI容器要獲取的資源的名稱,JNDI根據名稱去找到對應的資源后返回給應用程序。我們平時做javaEE開發時,服務器會為我們的應用程序創建很多資源,比如request對象,response對象,服務器創建的這些資源有兩種方式提供給我們的應用程序使用:第一種是通過方法參數的形式傳遞進來,比如我們在Servlet中寫的doPost和doGet方法中使用到的request對象和response對象就是服務器以參數的形式傳遞給我們的。第二種就是JNDI的方式,服務器把創建好的資源綁定到JNDI容器中去,應用程序想要使用資源時,就直接從JNDI容器中獲取相應的資源即可。
對於上面的name="jdbc/datasource"數據源資源,在應用程序中可以用如下的代碼去獲取
1 Context initCtx = new InitialContext(); 2 Context envCtx = (Context) initCtx.lookup("java:comp/env"); 3 dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此種配置下,數據庫的驅動jar文件需放置在tomcat的lib下
4.2、配置Tomcat數據源
1、在Web項目的WebRoot目錄下的META-INF目錄創建一個context.xml文件
2、在Web項目的WebRoot目錄下的META-INF目錄創建一個context.xml文件
<?xml version="1.0" encoding="UTF-8"?> <Context> <Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" username="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/testdb" maxActive="8" maxIdle="4"/> </Context>
3、將數據庫的驅動jar文件需放置在tomcat的lib下
4、在獲取數據庫連接的工具類(如jdbcUtils)的靜態代碼塊中獲取JNDI容器中的數據源
package cn.zy.utils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; public class JdbcUtils_JNDI { private static DataSource ds = null; //在靜態代碼塊中創建數據庫連接池 static{ try { //初始化JNDI Context initCtx = new InitialContext(); //得到JNDI容器 Context envCtx = (Context) initCtx.lookup("java:comp/env"); //從JNDI容器中檢索name為jdbc/datasource的數據源 ds = (DataSource)envCtx.lookup("jdbc/datasource"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /* * 從數據源中獲取連接 */ 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 (Exception e){ e.printStackTrace(); } if(st!=null){ try { st.close(); } catch (Exception e) { e.printStackTrace(); } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } }
寫一個Servlet進行測試:
package cn.zy.test; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.zy.utils.JdbcUtils_JNDI; public class JNDITest extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { //獲取連接 conn = JdbcUtils_JNDI.getConnection(); String sql = "insert into account(name,money) values(?,?)"; st = conn.prepareStatement(sql); st.setString(1, "F"); st.setFloat(2, 2000); st.executeUpdate(); response.getWriter().print("插入成功"); } catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils_JNDI.release(conn, st, rs); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }