1. 程式人生 > 資料庫 >Java資料庫連線池c3p0、dbcp和dbutils工具類的使用詳解

Java資料庫連線池c3p0、dbcp和dbutils工具類的使用詳解

一、資料庫連線池的簡單介紹

1.資料庫連線池簡介

在系統初始化時,將資料庫連線作為物件儲存在記憶體中,當用戶需要訪問資料庫時,並非建立一個新連線,而是從連線池中取出一個已建立的空閒連線物件。

使用完畢後,使用者也並非將連線關閉,而是將連線放回連線池中,以供下一個請求訪問使用。

而連線的建立、斷開都由連線池自身來管理。同時,還可以通過設定連線池的引數來控制連線池中的初始連線數、連線的上下限數以及每個連線的最大使用次數、最大空閒時間等等。也可以通過其自身的管理機制來監視資料庫連線的數量、使用情況。
資料庫連線池工作原理



2.資料庫連線池原理簡介

資料庫連線池的基本原理就是為資料庫建立一個緩衝池。在緩衝池中先建立指定數量的資料庫連線,當有連線請求時就從緩衝池中取出 處於空閒狀態的連線,並將此連線標記為忙碌。知道該請求程序結束後,它所使用的連線才會重新回到空閒狀態,並等待下一次請求呼叫。

所以綜上所述,資料庫連線池的主要作用就是負責分配、管理和釋放資料庫連線,它允許程式重複使用同一個現有的資料庫連線,大大縮短了執行時間,提高了執行效率。



3.使用資料庫連線池的必要性

當我們在進行基於資料庫的web程式開發時,我們可以現在主程式中通過JDBC的DriverManager建立資料庫連線,然後將要對資料庫進行操作的sql語句封裝到Statement中,最後在返回結果集後斷開資料庫連線。這種較為傳統的開發模式很容易造成安全隱患。

因為每一個數據庫連線使用完後都需要斷開連線,但如果程式出現異常導致 連線未能及時關閉,這樣就可能導致記憶體洩漏;另外使用傳統JDBC模式開發不能控制需要建立的連線數,系統一般會將資源大量分出給連線以防止資源不夠用,但是如果連線數超出一定數量也會有極大可能造成記憶體洩漏

問題

普通的JDBC資料庫連線使用DriverManager來獲取,每次向資料庫簡歷連線的時候都要將Connection載入到記憶體中,再根據JDBC程式碼(或配置檔案)中使用者名稱和密碼進行驗證。由此可見,如果同一個資料庫在同一時間數十人甚至上百人請求連線勢必會佔用大量系統資源,嚴重會導致伺服器崩潰




二、c3p0資料庫連線池的使用

1.定義

c3p0是一個開源的JDBC連線池,它實現了資料來源與JDNDI繫結,支援JDBC3規範和實現了JDBC2的擴充套件 說明的Connection和Statement池的DataSource物件。

即將用於連線資料庫的連線整合在一起行程一個隨取隨用的資料庫連線池(Connection pool)



2.使用

- 匯入jar包:c3p0的jar包和musq驅動jar包


- 配置配置檔案(兩種方式取一,放在src路徑下)

c3p0-config.xml

<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>

c3p0.properties

c3p0.driverClass = com.mysql.jdbc.Driver
c3p0.jdbcUrl = jdbc:mysql://localhost:3306/webexam
c3p0.user = root
c3p0.password =
c3p0.maxPoolSize = 20
c3p0.minPoolSize = 3
c3p0.maxStatements = 30
c3p0.maxIdleTime = 150

注:c3p0讀取配置優先順序如下(優先順序權重值從上到下)

  • Configuration values programmatically set. 【以程式設計方式設定配置值。】
  • System property setting of configuration value. 【配置值的系統屬性設定。】
  • Configuration values taken from the default configuration of a c3p0-config.xml file. 【配置值取自c3p0-config.xml檔案的預設配置。】
  • Configuration values specified in a c3p0.properties file 【在c3p0.properties檔案中指定的配置值】
  • c3p0’s hard-coded default values. 【c3p0的硬編碼預設值。】
    由此可見,配置檔案c3p0-config.xml是比c3p0.properties配置檔案優先順序高的,也是c3p0官方推薦的一種方式。

-c3p0工具類

public class C3P0Util {
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource("poolConnection");
	

	public static ComboPooledDataSource getDataSource(){
		return dataSource;
	}
	
	//從連線池中獲取連線
	public static Connection getConnection(){
		try {
			return (Connection) dataSource.getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	//將連線歸還連線池
	public static void release(Connection conn,Statement stmt,ResultSet rs){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs=null;
		}
		
		if(stmt!=null){
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			stmt=null;
		}
		
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
	
	
	public static void release(Connection conn,PreparedStatement ps,ResultSet rs){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs=null;
		}
		
		if(ps!=null){
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			ps=null;
		}
		
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
	
	public static void release(Connection conn){
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
}



三、dbcp資料庫連線池的使用

1.定義

DBCP(database connection pool)是Apache軟體基金組織下的開源連線池實現,該連線池依賴該組織下的另一個開源系統:Common-pool



2.使用

- 匯入jar包

commons-dbutils.jar
commons-pool.jar

- 配置檔案

dbcp.properties

#連線設定
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/student
username=root
password=666666
initialSize=10
maxActive=50
maxIdle=20
minIdle=5
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=utf8
defaultAutoCommit=true
defaultReadOnly=
defaultTransactionIsolation=REPEATABLE_READ

DBCPUtils工具類

public class DBCPUtils {
    static DataSource ds = null;
    static {
        try {
            // 得到配置檔案
            Properties prop = new Properties();
            prop.load(DBCPUtils.class.getClassLoader().getResourceAsStream(
                    "dbcpconfig.properties"));
            // 根據配置檔案建立資料庫連線池(資料來源)
            ds = BasicDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            throw new ExceptionInInitializerError("DBCP初始化異常,請檢查配置檔案!!!");
        }
    }

    /**
     * 得到資料庫連線物件
     * 
     * @return
     */
    public static Connection getConnection() {
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("伺服器忙。。。");
        }
    }

    /**
     * 關閉所有資源連線
     * 
     * @param conn
     * @param ps
     * @param rs
     */
    public static void releaseAll(Connection conn, Statement ps, ResultSet rs) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            conn = null;
        }

        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ps = null;
        }

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            rs = null;
        }
    }
}



四、dbutils工具類的簡單應用

1.DBUtil核心功能

QueryRunner中提供對sql語句操作的API

ResultSetHandler介面,用於定義select操作後,怎樣封裝結果集

DBUtils類是一個工具類,定義了關閉資源與事務處理的方法



2.QueryRunner核心類

QueryRunner(DataSource ds) 傳入引數為連線池

update(String sql,Objec…params) 執行insert update delete操作

query(String sql ,ResultSetHandler rsh,Object…params) 執行 select操作

3.ResultSetHandler結果集處理類

在這裡插入圖片描述



## **4.例項程式碼** ### -關於增刪改查
/*
 *  使用QueryRunner類,實現對資料表的
 *  insert delete update
 *  呼叫QueryRunner類的方法 update (Connection con,String sql,Object...param)
 *  Object...param 可變引數,Object型別,SQL語句會出現?佔位符
 *  資料庫連線物件,自定義的工具類傳遞
*/
public class QueryRunnerDemo {
	private static Connection con = JDBCUtilConfig.getConnection();
	
	public static void main(String[] args)throws SQLException {
		//insert();
		update();
		//delete();
	}
	/*
	 *  定義方法,使用QueryRunner類的方法delete將資料表的資料刪除
	 */
	public static void delete()throws SQLException{
		//建立QueryRunner類物件
		QueryRunner qr = new QueryRunner();	
		//寫刪除的SQL語句
		String sql = "DELETE FROM sort WHERE sid=?";
		//呼叫QueryRunner方法update
		int row = qr.update(con, sql, 8);
		System.out.println(row);
		/*
		 *  判斷insert,update,delete執行是否成功
		 *  對返回值row判斷
		 *  if(row>0) 執行成功
		 */
		DbUtils.closeQuietly(con);
	}
	
	/*
	 *  定義方法,使用QueryRunner類的方法update將資料表的資料修改
	 */
	public static void update()throws SQLException{
		//建立QueryRunner類物件
		QueryRunner qr = new QueryRunner();	
		//寫修改資料的SQL語句
		String sql = "UPDATE sort SET sname=?,sprice=?,sdesc=? WHERE sid=?";
		//定義Object陣列,儲存?中的引數
		Object[] params = {"花卉",100.88,"情人節玫瑰花",4};
		//呼叫QueryRunner方法update
		int row = qr.update(con, sql, params);
		System.out.println(row);
		DbUtils.closeQuietly(con);
	}
	
	
	/*
	 * 定義方法,使用QueryRunner類的方法update向資料表中,新增資料
	 */
	public static void insert()throws SQLException{
		//建立QueryRunner類物件
		QueryRunner qr = new QueryRunner();
		String sql = "INSERT INTO sort (sname,sprice,sdesc)VALUES(?,?,?)";
		//將三個?佔位符的實際引數,寫在陣列中
		Object[] params = {"體育用品",289.32,"購買體育用品"};
		//呼叫QueryRunner類的方法update執行SQL語句
		int row = qr.update(con, sql, params);
		System.out.println(row);
		DbUtils.closeQuietly(con);
	}
}

-關於查詢

category.java

// JavaBean
public class Category {
	private String cid;
	private String cname;
	
	public String getCid() {
		return cid;
	}
	public void setCid(String cid) {
		this.cid = cid;
	}
	
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	@Override
	public String toString() {
		return "Category [cid=" + cid + ", cname=" + cname + "]";
	}
	public Category() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Category(String cid, String cname) {
		super();
		this.cid = cid;
		this.cname = cname;
	}
}

C3P0Utils.java
/**
 * 在C3P0連線池中 遵循了javax.sql.DataSource介面的實現類:
 * 		ComboPooledDataSource
*/
import java.beans.PropertyVetoException;
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 C3P0Utils {

	private static ComboPooledDataSource ds = new ComboPooledDataSource();	
	// 自動載入C3P0-config.xml檔案

	public static DataSource getDataSource(){
		return ds;
	}
	
	public static Connection getConnection() throws SQLException{
		// 獲取連線,從C3P0連線池獲取
		return ds.getConnection();
	}
	
	// 關閉所有資源的統一程式碼
	public static void closeAll(Connection conn, Statement st, ResultSet rs){
		//負責關閉
		if(conn != null){
			try {
				conn.close();		// 使用代理,放回到連線池中
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}

DBUtils.java
package cn.simon.jdbc.demo03_DBUtils的使用;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import cn.simon.jdbc.domain.Category;

/**
 *  DBUtils執行資料庫查詢操作
 *  	1.QueryRunner(DataSource)
 *  	2.query(String sql, ResultSetHandler<T> rsh, Object... params); // 主要執行查詢
*/

public class DBUtilsDemo {
	public static void main(String[] args) throws SQLException {
		// TODO Auto-generated method stub

//		queryDemo01();
//		queryDemo02();
//		queryDemo03();
//		queryDemo04();
//		queryDemo05();
		queryDemo06();
//		queryDemo07();
//		queryDemo08();
	}
	
	// ArrayHandler處理類的使用
	public static void queryDemo01() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		Object[] objs = qr.query("select * from category where cid = ?", new ArrayHandler(), 1);
		for(Object o: objs){		// object[]中儲存了object物件
			System.out.println(o);
		}
	}
	
	// ArrayListHandler
	public static void queryDemo02() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		List<Object[]> objs = qr.query("select * from category ", new ArrayListHandler());
		for (Object[] objects : objs) {
			System.out.println(objects[0]+"\t"+objects[1]);
		}
	}
	
	// BeanHandler處理類的使用
	public static void queryDemo03() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select * from category";
		Category c = qr.query(sql, new BeanHandler<Category>(Category.class));
		System.out.println(c);
	}
	
	// BeanListHandler
	public static void queryDemo04() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select * from category";
		List<Category> c = qr.query(sql, new BeanListHandler<Category>(Category.class));
		for (Category category : c) {
			System.out.println(category);
		}
	}
	
	// ColumnListHandler處理類的使用
	public static void queryDemo05() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select * from category";
		List<Object> c = qr.query(sql, new ColumnListHandler<Object>("cname"));
		System.out.println(c);
	}
	
	// MapHandler處理類的使用
	public static void queryDemo06() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select * from category";
		Map<String, Object> map = qr.query(sql, new MapHandler());
		// 3.
		System.out.println(map);
	}
	
	// MapListHandler處理類的使用
	public static void queryDemo07() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select * from category";
		List<Map<String, Object>> maps = qr.query(sql, new MapListHandler());
		// 3.List
		System.out.println(maps);
	}
	
	// MapListHandler處理類的使用
	public static void queryDemo08() throws SQLException{
		// 1.建立QueryRunner物件
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.執行查詢
		String sql = "select count(*) from category";
		Long count = qr.query(sql, new ScalarHandler<Long>());
		// 3.List
		System.out.println(count);
	}
}



五、文章引用/參考

https://www.mchange.com/projects/c3p0/index.html
https://www.cnblogs.com/SUN99bk/p/12181493.html
https://www.cnblogs.com/ygj0930/p/6405861.html
https://www.cnblogs.com/xww0826/p/10359479.html#1%E3%80%81DBCP%E8%BF%9E%E6%8E%A5%E6%B1%A0
https://www.cnblogs.com/lztkdr/p/DBCPUtils.html
http://commons.apache.org/proper/commons-dbcp/configuration.html
https://www.jb51.net/article/101072.htm
https://www.cnblogs.com/sunseine/p/5947448.html
https://blog.csdn.net/simonforfuture/article/details/90480147
https://blog.csdn.net/a911711054/article/details/77719685?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase
https://blog.csdn.net/qq_25106373/article/details/80955184



歡迎關注微信公眾號【Java程式設計扉頁】
更多精彩,且讓我慢慢與你分享
微信公眾號【Java程式設計扉頁】