JDBC既制定統一標準兼容了多種數據庫,又利用預報告堵上了SQL注入漏洞,照理說已經很完善了,但是人算不如天算,它在性能方面不盡如人意。問題出在數據庫鏈接的管理上,按照正常流程,每次操做完數據庫,都要關閉鏈接,不管是代碼裏手工關閉,仍是由try語句自動關閉。若是沒有及時關閉數據庫鏈接,就會長時間佔用有限的數據庫內存,導致無謂的系統資源浪費。然而頻繁開關數據庫鏈接也有毛病,由於每次獲取操做都要CPU處理,常常鏈接數據庫會加劇CPU的負擔。看來內存與CPU像是一對難兄難弟,無論怎麼作都會影響其中一個,正所謂魚與熊掌不可兼得。
其實鏈接跟線程的狀況類似,線程也很頭疼頻繁建立致使的資源開銷,爲此Java早早就設計了線程池機制,事先在一個池子中容納若干線程,須要使用線程時便從中挑一根線程執行任務,任務作完再歸還線程,如此實現了線程資源的循環利用,有效提升了系統的總體運行效率。既然線程們組建了線程池這個你們庭,那麼鏈接們可否也組成鏈接池的你們庭呢?Java當然自帶了線程池工具,卻未能推出相似的鏈接池工具,因而各類第三方的鏈接池蜂擁而起,例如DBCP、C3P0、Proxool等等,其中應用普遍的當數C3P0。
C3P0是一個開源的數據庫鏈接池,它支持JDBC3規範和JDBC2的標準擴展。若要在Java工程中運用C3P0,得先導入它的jar包,好比c3p0-0.9.5.4.jar,同時還要導入該jar包依賴的mchange-commons-java-0.2.16.jar,也就是一共導入兩個jar文件。使用C3P0很簡單,掌握ComboPooledDataSource類的用法就夠了,該類的常見方法說明以下:
setDriverClass:設置鏈接池的數據庫驅動。
setJdbcUrl:設置數據庫的鏈接地址。
setUser:設置數據庫的用戶名。
setPassword:設置數據庫的密碼。
setMaxPoolSize:設置鏈接池大小的上限。
setMinPoolSize:設置鏈接池大小的下限。
setInitialPoolSize:設置鏈接池的初始大小。
setMaxStatements:設置報告的最大個數。
setCheckoutTimeout:設置獲取鏈接的等待時間,單位毫秒。當鏈接池中的全部鏈接都被佔用的時候,新請求想獲取鏈接就必須等待,等待現有鏈接被釋放後才能獲取空閒鏈接。默認爲0表示一直等待下去。
setMaxIdleTime:設置最大空閒時間,單位秒。若是某個鏈接超過該時間仍未使用,則會被自動回收。默認爲0表示不判斷是否超時,也就是永不回收。
getConnection:從鏈接池中獲取一個鏈接。
close:關閉鏈接池。
引入鏈接池以後,完整的數據庫操做流程分解成了兩大步驟:初始化鏈接池、從鏈接池中取出一個鏈接處理,下面分別予以介紹。
一、初始化鏈接池
該步驟首先建立C3P0鏈接池的對象,再依次調用相關方法設置詳細的參數信息,包括數據庫驅動、鏈接地址、用戶名、密碼,以及與鏈接池有關的規格參數。下面是初始化C3P0鏈接池的代碼例子:html
private static ComboPooledDataSource dataSource; // 聲明C3P0鏈接池的對象 // 初始化鏈接池 private static void initDataSource() { dataSource = new ComboPooledDataSource(); // 建立C3P0鏈接池 try { dataSource.setDriverClass(driver_class); // 設置鏈接池的數據庫驅動 } catch (PropertyVetoException e) { e.printStackTrace(); } dataSource.setJdbcUrl(dbUrl); // 設置數據庫的鏈接地址 dataSource.setUser(dbUserName); // 設置數據庫的用戶名 dataSource.setPassword(dbPassword); // 設置數據庫的密碼 dataSource.setMaxPoolSize(10); // 設置鏈接池大小的上限 dataSource.setMinPoolSize(1); // 設置鏈接池大小的下限 dataSource.setInitialPoolSize(3); // 設置鏈接池的初始大小 }
二、從鏈接池中取出一個鏈接處理
除了一開始調用鏈接池的getConnection獲取鏈接以外,該步驟剩餘的操做過程與JDBC原有流程保持一致,即得到數據庫鏈接以後,一樣要建立鏈接的報告,而後命令報告執行SQL語句。下面是經過鏈接池操做數據庫的代碼例子:java
// 顯示性別分組 private static void showRecordGroupBySex() { String sql = "select sex,count(1) count from teacher group by sex order by sex asc"; // 從鏈接池中獲取鏈接、建立鏈接的報告、命令報告執行指定的SQL語句 try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { // 循環遍歷結果集裏面的全部記錄 int sex = rs.getInt("sex"); // 獲取指定字段的整型值 int count = rs.getInt("count"); // 獲取指定字段的整型值 String desc = String.format("%s老師有%d位;", sex==0 ? "男" : "女", count); System.out.print(desc); } } catch (SQLException e) { e.printStackTrace(); } }
整合鏈接池的初始化和具體操做的代碼,運行包含整合代碼以內的測試程序,觀察以下日誌可知C3P0鏈接池正常工做。sql
男老師有2位;女老師有3位;
更多Java技術文章參見《Java開發筆記(序)章節目錄》數據庫