您好,登錄后才能下訂單哦!
一種技術的出現(xiàn),要么是解決實際問題,要么是優(yōu)化現(xiàn)有技術。數(shù)據(jù)庫連接池技術的出現(xiàn),是為了優(yōu)化數(shù)據(jù)庫連接操作的性能。
在使用JDBC進行數(shù)據(jù)庫開發(fā)的時候,一般經歷這樣一個過程:
1)加載數(shù)據(jù)庫的驅動
2)建立數(shù)據(jù)庫的連接(Connection)
3)創(chuàng)建SQL語句聲明(Statement)
4)執(zhí)行更新(executeUpdate)或查詢(executeQuery)
本文中講的數(shù)據(jù)庫連接池,只是針對Connection的部分的優(yōu)化。
學習連接池
a. 自定義一個連接池
b. 學習優(yōu)秀的連接池組件
1)DBCP
2)C3P0
思考:程序中Connection連接是如何管理的?
數(shù)據(jù)庫的連接(Connection)涉及到的操作有:a)數(shù)據(jù)庫操作開始,創(chuàng)建連接,b)操作結束,關閉連接。
我們知道連接資源十分寶貴,因此需要對它進行管理。如果頻繁的打開和關閉連接,會影響程序的運行效率!
連接管理的思路:預先創(chuàng)建一組連接,用的時候每次取出一個;用完之后,將連接放回去。
第一個版本
package com.rk.pool; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; /** * 自定義連接池,管理連接(Connection) * 全局參數(shù):初始化數(shù)目、最大連接數(shù)、當前連接數(shù)、連接池集合 * * 當啟動的時候,就有3(init_count)個初始連接Connection * 1.創(chuàng)建連接的方法createConnection() * 2.提供獲取連接的方法getConnection() * 2.1如果有空閑連接,則直接返回 * 2.2如果沒有空閑連接,且沒有達到最大限制數(shù)量,則創(chuàng)建連接返回 * 2.3如果沒有空閑連接,且連接數(shù)據(jù)達到最大限制數(shù),則無法獲取到連接,返回null * 3.提供釋放連接的方法releaseConnection(Connection conn) * 3.1如果連接池內的空閑連接數(shù)量小于初始的連接數(shù)量,則當前連接返回到連接池中 * 3.2如果連接池內的空閑連接數(shù)量等于初始的連接數(shù)量,則將當前連接關閉 * @author lsieun * */ public class MyConnectionPool { private final static String url = "jdbc:mysql:///testdb"; private final static String driverClassName = "com.mysql.jdbc.Driver"; private final static String user = "root"; private final static String password = "root"; private LinkedList<Connection> freeConnections = null;//// 連接池 (存放所有的初始化連接) private final int init_count = 3;//初始的連接(Connection)數(shù)量(最小值) private final int max_count = 6;//最大的連接數(shù)量(最大值) private int current_count = 0;//當前擁有的連接數(shù)量(當前值) /** * 靜態(tài)代碼塊,加載數(shù)據(jù)庫的驅動程序 */ static { try { Class.forName(driverClassName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } //1.初始化 public MyConnectionPool() throws SQLException { //初始化連接池 freeConnections = new LinkedList<Connection>(); //將指定數(shù)量(init_cont)加入到連接池中 for(int i=0;i<init_count;i++) { // 記錄當前連接數(shù)目 current_count++; // 創(chuàng)建連接對象 Connection con = createConnection(); // 把連接加入連接池 freeConnections.add(con); } } public int getCurrentCount() { return current_count; } /** * 私有的靜態(tài)方法,用于創(chuàng)建數(shù)據(jù)庫連接 */ private static Connection createConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } //2.獲取數(shù)據(jù)庫的連接 public Connection getConnection() throws SQLException { //判斷連接池中是否有連接, 如果有連接,就直接從連接池取出 if(freeConnections.size()>0) { return freeConnections.removeFirst(); } else if(current_count<max_count)//連接池中沒有連接;并且如果沒有達到最大連接數(shù),創(chuàng)建新連接; { current_count++; return createConnection(); } else//當前已經達到最大連接數(shù) { System.out.println("已經達到連接最大數(shù)量限制,無法獲得新的連接"); return null; } } //3.釋放連接 public void releaseConnection(Connection conn) throws SQLException { //池的數(shù)目如果小于初始化連接,就放入池中 if(freeConnections.size() < init_count) { freeConnections.addLast(conn); } else {//關閉 current_count--; conn.close(); } } public static void main(String[] args) throws SQLException { /* MyConnectionPool pool = new MyConnectionPool(); System.out.println("0-->" + pool.getCurrentCount()); Connection conn1 = pool.getConnection(); Connection conn2 = pool.getConnection(); Connection conn3 = pool.getConnection(); System.out.println("3-->" + pool.getCurrentCount()); Connection conn4 = pool.getConnection(); System.out.println("4-->" + pool.getCurrentCount()); Connection conn5 = pool.getConnection(); System.out.println("5-->" + pool.getCurrentCount()); Connection conn6 = pool.getConnection(); System.out.println("6-->" + pool.getCurrentCount()); Connection conn7 = pool.getConnection(); System.out.println("7-->" + pool.getCurrentCount()); */ MyConnectionPool pool = new MyConnectionPool(); System.out.println("0-->" + pool.getCurrentCount()); Connection conn1 = pool.getConnection(); Connection conn2 = pool.getConnection(); Connection conn3 = pool.getConnection(); System.out.println("3-->" + pool.getCurrentCount()); pool.releaseConnection(conn1); Connection conn4 = pool.getConnection(); System.out.println("4-->" + pool.getCurrentCount()); } }
第二個版本(在創(chuàng)建Connection時,增加了動態(tài)代理)
如果開發(fā)人員得到Connection對象時,并調用它的close方法,并不會關閉連接,而是將Connection對象放回到連接池中,這就是通過動態(tài)代理來實現(xiàn)的。
如果對某個接口中的某個指定的方法的功能進行擴展,而不想實現(xiàn)接口里所有方法,可以使用(動態(tài))代理模式! 使用動態(tài)代理,可以監(jiān)測接口中方法的執(zhí)行!
Java中代理模式:靜態(tài)/動態(tài)/Cglib代理(spring)
如何對Connection對象,生成一個代理對象:
|--Proxy
static Object newProxyInstance(
ClassLoader loader, 當前使用的類加載器
Class<?>[] interfaces, 目標對象(Connection)實現(xiàn)的接口類型
InvocationHandler h 事件處理器:當執(zhí)行上面接口中的方法的時候,就會自動觸發(fā)事件處理器代碼,把當前執(zhí)行的方法(method)作為參數(shù)傳入。
)
package com.rk.pool; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; /** * 自定義連接池,管理連接(Connection) * 全局參數(shù):初始化數(shù)目、最大連接數(shù)、當前連接數(shù)、連接池集合 * * 當啟動的時候,就有3(init_count)個初始連接Connection * 1.創(chuàng)建連接的方法createConnection() * 2.提供獲取連接的方法getConnection() * 2.1如果有空閑連接,則直接返回 * 2.2如果沒有空閑連接,且沒有達到最大限制數(shù)量,則創(chuàng)建連接返回 * 2.3如果沒有空閑連接,且連接數(shù)據(jù)達到最大限制數(shù),則無法獲取到連接,返回null * 3.提供釋放連接的方法releaseConnection(Connection conn) * 3.1如果連接池內的空閑連接數(shù)量小于初始的連接數(shù)量,則當前連接返回到連接池中 * 3.2如果連接池內的空閑連接數(shù)量等于初始的連接數(shù)量,則將當前連接關閉 * @author lsieun * */ public class MyConnectionPoolUpdate { private final static String url = "jdbc:mysql:///testdb"; private final static String driverClassName = "com.mysql.jdbc.Driver"; private final static String user = "root"; private final static String password = "root"; private LinkedList<Connection> freeConnections = null;//// 連接池 (存放所有的初始化連接) private final int init_count = 3;//初始的連接(Connection)數(shù)量(最小值) private final int max_count = 6;//最大的連接數(shù)量(最大值) private int current_count = 0;//當前擁有的連接數(shù)量(當前值) /** * 靜態(tài)代碼塊,加載數(shù)據(jù)庫的驅動程序 */ static { try { Class.forName(driverClassName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } //1.初始化 public MyConnectionPoolUpdate() throws SQLException { //初始化連接池 freeConnections = new LinkedList<Connection>(); //將指定數(shù)量(init_cont)加入到連接池中 for(int i=0;i<init_count;i++) { // 記錄當前連接數(shù)目 current_count++; // 創(chuàng)建連接對象 Connection con = createConnection(); // 把連接加入連接池 freeConnections.add(con); } } public int getCurrentCount() { return current_count; } /** * 私有的方法,用于創(chuàng)建數(shù)據(jù)庫連接 */ private Connection createConnection() throws SQLException { final Connection conn = DriverManager.getConnection(url, user, password); Connection proxy = (Connection)Proxy.newProxyInstance( //conn.getClass().getClassLoader();// 第一個參數(shù),類加載器(方法一) Connection.class.getClassLoader(), // 第一個參數(shù),類加載器(方法二) //conn.getClass().getInterfaces(),//第二個參數(shù),如果當前目標對象是一個具體的類的時候,用這個 new Class[]{Connection.class}, //第二個參數(shù),由于Connection是一個接口 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法返回值 Object result = null; System.out.println(proxy.getClass()); // 當前執(zhí)行的方法的方法名 String methodName = method.getName(); if("close".equals(methodName)) { System.out.println("begin:當前執(zhí)行close方法開始!"); // 調用釋放連接池的方法 MyConnectionPoolUpdate.this.releaseConnection(conn); System.out.println("end: 當前連接已經放入連接池了!"); } else { // 調用目標對象方法 result = method.invoke(conn, args); } return result; } }); return proxy; } //2.獲取數(shù)據(jù)庫的連接 public Connection getConnection() throws SQLException { //判斷連接池中是否有連接, 如果有連接,就直接從連接池取出 if(freeConnections.size()>0) { return freeConnections.removeFirst(); } else if(current_count<max_count)//連接池中沒有連接;并且如果沒有達到最大連接數(shù),創(chuàng)建新連接; { current_count++; return createConnection(); } else//當前已經達到最大連接數(shù) { System.out.println("已經達到連接最大數(shù)量限制,無法獲得新的連接"); return null; } } //3.釋放連接 public void releaseConnection(Connection conn) throws SQLException { System.out.println("MyConnectionPoolUpdate.releaseConnection()"); //池的數(shù)目如果小于初始化連接,就放入池中 if(freeConnections.size() < init_count) { freeConnections.addLast(conn); } else {//關閉 current_count--; conn.close(); } } public static void main(String[] args) throws SQLException { MyConnectionPoolUpdate pool = new MyConnectionPoolUpdate(); System.out.println("0-->" + pool.getCurrentCount()); Connection conn1 = pool.getConnection(); conn1.close(); } }
Sun公司約定: 如果是連接池技術,需要實現(xiàn)一個接口:javax.sql.DataSource!
注意,DataSource位于javax.sql包下,而不是java.sql包下,其中x表示擴展的意思。
下面是DataSource的源代碼,它只提供了兩個重載方法getConnection。
public interface DataSource extends CommonDataSource,Wrapper { /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. */ Connection getConnection() throws SQLException; /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. */ Connection getConnection(String username, String password) throws SQLException; }
javax.sql.DataSource接口
(1)DataSource object是對DriverManager的一種替代。推薦使用DataSource來獲得Connection對象。
An alternative to the DriverManager facility, a DataSource object is the preferred means of getting a connection.
(2)實現(xiàn)DataSource接口的object常常是用JNDI注冊的。
An object that implements the DataSource interface will typically be registered with a naming service based on the JavaTM Naming and Directory (JNDI) API.
(3)DataSource接口由driver vendor實現(xiàn),有三種實現(xiàn)類型:
3.1)Basic implementation:基本實現(xiàn),產生標準的Connection對象
3.2)Connection pooling implementation:連接池實現(xiàn),有connection pool,由a middle-tier connection pooling manager
3.3)Distributed transaction implementation:分布式事務實現(xiàn),由a middle-tier transaction manager和a connection pooling manager.
The DataSource interface is implemented by a driver vendor. There are three types of implementations:
a)Basic implementation -- produces a standard Connection object
b)Connection pooling implementation -- produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager.
c)Distributed transaction implementation -- produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.
(4)通過DataSource獲得的driver,并不會用DriverManager注冊。
回憶一下:DriverManager可以注冊驅動。
Driver driver = new com.mysql.jdbc.Driver();
//注冊驅動程序(可以注冊多個驅動程序)
DriverManager.registerDriver(driver);
A driver that is accessed via a DataSource object does not register itself with the DriverManager. Rather, a DataSource object is retrieved though a lookup operation and then used to create a Connection object.
(5)如果是basic implementation,那么通過DataSource object獲得的Connection與通過DriverManager獲得的Connection是一樣的。
With a basic implementation, the connection obtained through a DataSource object is identical to a connection obtained through the DriverManager facility.
DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn),使用DBCP數(shù)據(jù)源,應用程序應在系統(tǒng)中增加如下兩個 jar 文件:
Commons-dbcp.jar:連接池的實現(xiàn)
Commons-pool.jar:連接池實現(xiàn)的依賴庫
Tomcat 的連接池正是采用該連接池來實現(xiàn)的。該數(shù)據(jù)庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。
核心類:BasicDataSource
使用步驟
引入jar文件
commons-dbcp-1.4.jar
http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
commons-pool-1.6.jar
http://commons.apache.org/proper/commons-pool/download_pool.cgi
使用連接池,創(chuàng)建連接
a) 硬編碼方式
b) 配置方式(db.properties)
package com.rk.dbcp; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.junit.Test; public class Demo { // 1. 硬編碼方式實現(xiàn)連接池 @Test public void testDbcp() throws SQLException { // DBCP連接池核心類 BasicDataSource dataSource = new BasicDataSource(); // 連接池參數(shù)配置: 連接字符串、驅動、用戶、密碼 dataSource.setUrl("jdbc:mysql:///testdb");//"jdbc:mysql://localhost:3306/testdb" dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUsername("root"); dataSource.setPassword("root"); // 連接池參數(shù)配置:初始化連接數(shù)、最大連接數(shù) 、最大空閑時間 dataSource.setInitialSize(3); dataSource.setMaxActive(6); dataSource.setMaxIdle(3000); // 獲取連接 Connection conn = dataSource.getConnection(); System.out.println(conn); conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate(); // 關閉 conn.close(); } @Test // 2. 【推薦】配置方式實現(xiàn)連接池 , 便于維護 public void testProp() throws Exception { // 獲取文件流 InputStream inStream = Demo.class.getResourceAsStream("db.properties"); //創(chuàng)建Properties對象 Properties prop = new Properties(); // 加載屬性配置文件 prop.load(inStream); // 根據(jù)prop配置,直接創(chuàng)建數(shù)據(jù)源對象 DataSource dataSource = BasicDataSourceFactory.createDataSource(prop); // 獲取連接 Connection conn = dataSource.getConnection(); System.out.println(conn); conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate(); // 關閉 conn.close(); } }
配置方式實現(xiàn)DBCP連接池, 配置文件中的key與BaseDataSouce中的屬性一樣:
url=jdbc:mysql://localhost:3306/testdb driverClassName=com.mysql.jdbc.Driver username=root password=root initialSize=3 maxActive=6 maxIdle=3000
org.apache.commons.dbcp.BasicDataSource
(1)Apache的DBCP中的核心類BasicDataSource是對javax.sql.DataSource接口的Basic implementation。
Basic implementation of javax.sql.DataSource that is configured via JavaBeans properties.
org.apache.commons.dbcp.BasicDataSourceFactory
(1)BasicDataSourceFactory是一個JNDI object factory,用于創(chuàng)建一個BasicDataSource實例。
JNDI object factory that creates an instance of BasicDataSource.
C3P0連接池:最常用的連接池技術!Spring框架,默認支持C3P0連接池技術!
核心類:ComboPooledDataSource
使用:
1.下載,引入jar文件: c3p0-0.9.1.2.jar
https://sourceforge.net/projects/c3p0/
http://www.mchange.com/projects/c3p0/
2. 使用連接池,創(chuàng)建連接
a) 硬編碼方式
b) 配置方式(xml)
package com.rk.c3p0; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; public class Demo { @Test //1. 硬編碼方式,使用C3P0連接池管理連接 public void testCode() throws PropertyVetoException, SQLException { // 創(chuàng)建連接池核心工具類 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 設置連接參數(shù):url、驅動、用戶密碼、初始連接數(shù)、最大連接數(shù) dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testdb"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setUser("root"); dataSource.setPassword("root"); dataSource.setInitialPoolSize(3); dataSource.setMaxPoolSize(6); dataSource.setMaxIdleTime(1000); // ---> 從連接池對象中,獲取連接對象 Connection conn = dataSource.getConnection(); System.out.println(conn); conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate(); // 關閉 conn.close(); } @Test //2. XML配置方式,使用C3P0連接池管理連接 public void testXML() throws SQLException { // 創(chuàng)建c3p0連接池核心工具類 // 自動加載src下c3p0的配置文件【c3p0-config.xml】 ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用默認的配置 //ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle_config");// 使用指定的配置 // ---> 從連接池對象中,獲取連接對象 Connection conn = dataSource.getConnection(); System.out.println(conn); conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate(); // 關閉 conn.close(); } }
c3p0-config.xml文件放在src目錄下
<c3p0-config> <default-config> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">3</property> <property name="maxPoolSize">6</property> <property name="maxIdleTime">1000</property> </default-config> <named-config name="oracle_config"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="initialPoolSize">3</property> <property name="maxPoolSize">6</property> <property name="maxIdleTime">1000</property> </named-config> </c3p0-config>
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。