连接池 DBCP c3p0以及分页的案例
1. 連接池
思考:
程序中連接如何管理?
- 連接資源寶貴;需要對連接管理
- 連接:
a) 操作數據庫,創建連接
b) 操作結束, 關閉!
分析:
涉及頻繁的連接的打開、關閉,影響程序的運行效率!
連接管理:
預先創建一組連接,有的時候每次取出一個; 用完后,放回;
學習連接池:
- 自定義一個連接池
- 學習優秀的連接池組件
a) DBCP
b) C3P0
自定義連接池
代理:
如果對某個接口中的某個指定的方法的功能進行擴展,而不想實現接口里所有方法,可以使用(動態)代理模式!
Java中代理模式:靜態/動態/Cglib代理(spring)
使用動態代理,可以監測接口中方法的執行!
如何對Connection對象,生成一個代理對象:
|--Proxy
static Object newProxyInstance(
ClassLoader loader, 當前使用的類加載器
Class<?>[] interfaces, 目標對象(Connection)實現的接口類型
InvocationHandler h 事件處理器:當執行上面接口中的方法的時候,就會自動觸發事件處理器代碼,把當前執行的方法(method)作為參數傳入。
) 想要什么就努力的去得到,去想方設法,去行動,最終得到之。
說說代理Proxy這里到底干了什么,這里就是什么呢,可以使用pool.releaseConnection(conn)釋放鏈接,也可以鏈接本身直接conn.close()釋放鏈接,自身直接釋放鏈接的,不會改變pool中的屬性。Proxy的作用就是一種代理,是一種基于時間的機制,代理完成一些業務邏輯,當你執行某個方法,比如這里執行close方法的時候,我就要讓你順帶做一些改變pool屬性的一些邏輯。
|
/** * 自定義連接池, 管理連接 * 代碼實現: 1. MyPool.java 連接池類, 2. 指定全局參數: 初始化數目、最大連接數、當前連接、 連接池集合 3. 構造函數:循環創建3個連接 4. 寫一個創建連接的方法 5. 獲取連接 ------> 判斷: 池中有連接, 直接拿 ------> 池中沒有連接, ------> 判斷,是否達到最大連接數; 達到,拋出異常;沒有達到最大連接數, 創建新的連接 6. 釋放連接 -------> 連接放回集合中(..) * */ public class MyPool { private int init_count = 3; // 初始化連接數目 private int max_count = 6; // 最大連接數 private int current_count = 0; // 記錄當前使用連接數 // 連接池 (存放所有的初始化連接) private LinkedList<Connection> pool = new LinkedList<Connection>(); //1. 構造函數中,初始化連接放入連接池 public MyPool() { // 初始化連接 for (int i=0; i<init_count; i++){ // 記錄當前連接數目 current_count++; // 創建原始的連接對象 Connection con = createConnection(); // 把連接加入連接池 pool.addLast(con); } } //2. 創建一個新的連接的方法 private Connection createConnection(){ try { Class.forName("com.mysql.jdbc.Driver"); // 原始的目標對象 final Connection con = DriverManager.getConnection("jdbc:mysql:///jdbc_demo", "root", "root"); /**********對con對象代理**************/ // 對con創建其代理對象 Connection proxy = (Connection) Proxy.newProxyInstance( con.getClass().getClassLoader(), // 類加載器 //con.getClass().getInterfaces(), // 當目標對象是一個具體的類的時候 new Class[]{Connection.class}, // 目標對象實現的接口 new InvocationHandler() { // 當調用con對象方法的時候, 自動觸發事務處理器 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法返回值 Object result = null; // 當前執行的方法的方法名 String methodName = method.getName(); // 判斷當執行了close方法的時候,把連接放入連接池 if ("close".equals(methodName)) { System.out.println("begin:當前執行close方法開始!"); // 連接放入連接池 pool.addLast(con); System.out.println("end: 當前連接已經放入連接池了!"); } else { // 調用目標對象方法 result = method.invoke(con, args); } return result; } } ); return proxy;// 就是上面定義的 final Connection } catch (Exception e) { throw new RuntimeException(e); } } //3. 獲取連接 public Connection getConnection(){ // 3.1 判斷連接池中是否有連接, 如果有連接,就直接從連接池取出 if (pool.size() > 0){ return pool.removeFirst(); } // 3.2 連接池中沒有連接: 判斷,如果沒有達到最大連接數,創建; if (current_count < max_count) { // 記錄當前使用的連接數 current_count++; // 創建連接 return createConnection(); } // 3.3 如果當前已經達到最大連接數,拋出異常 throw new RuntimeException("當前連接已經達到最大連接數目 !"); } //4. 釋放連接 public void realeaseConnection(Connection con) { // 4.1 判斷: 池的數目如果小于初始化連接,就放入池中 if (pool.size() < init_count){ pool.addLast(con); } else { try { // 4.2 關閉 current_count--; con.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } public static void main(String[] args) throws SQLException { MyPool pool = new MyPool(); System.out.println("當前連接: " + pool.current_count); // 3 // 使用連接 pool.getConnection(); pool.getConnection(); Connection con4 = pool.getConnection(); Connection con3 = pool.getConnection(); Connection con2 = pool.getConnection(); Connection con1 = pool.getConnection(); // 釋放連接, 連接放回連接池 // pool.realeaseConnection(con1); /* * 希望:當關閉連接的時候,要把連接放入連接池!【當調用Connection接口的close方法時候,希望觸發pool.addLast(con);操作】 * 把連接放入連接池 * 解決1:實現Connection接口,重寫close方法 * 解決2:動態代理 */ con1.close(); // 再獲取 pool.getConnection(); System.out.println("連接池:" + pool.pool.size()); // 0 System.out.println("當前連接: " + pool.current_count); // 3 } } |
代理的總結:(了解會用)
使用代理,可以在不實現接口的情況,對接口的方法進行擴展,添加額外的用戶需要的業務邏輯!
2.開源的連接池技術
概述:
Sun公司約定: 如果是連接池技術,需要實現一個接口!
javax.sql.DataSource;
連接池:
DBCP
C3P0
2.1 DBCP連接池:
l DBCP 是 Apache 軟件基金組織下的開源連接池實現,使用DBCP數據源,應用程序應在系統中增加如下兩個 jar 文件:
- Commons-dbcp.jar:連接池的實現
- Commons-pool.jar:連接池實現的依賴庫
l Tomcat 的連接池正是采用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。
l 核心類:BasicDataSource、BasicDataSourceFactory
l 使用步驟
- 引入jar文件
l commons-dbcp-1.4.jar
l commons-pool-1.5.6.jar
|
public class App_DBCP { // 1. 硬編碼方式實現連接池 @Test public void testDbcp() throws Exception { // DBCP連接池核心類 BasicDataSource dataSouce = new BasicDataSource(); // 連接池參數配置:初始化連接數、最大連接數 / 連接字符串、驅動、用戶、密碼 dataSouce.setUrl("jdbc:mysql:///jdbc_demo"); //數據庫連接字符串 dataSouce.setDriverClassName("com.mysql.jdbc.Driver"); //數據庫驅動 dataSouce.setUsername("root"); //數據庫連接用戶 dataSouce.setPassword("root"); //數據庫連接密碼 dataSouce.setInitialSize(3); // 初始化連接 dataSouce.setMaxActive(6); // 最大連接 dataSouce.setMaxIdle(3000); // 最大空閑時間 // 獲取連接 Connection con = dataSouce.getConnection(); con.prepareStatement("delete from admin where id=3").executeUpdate(); // 關閉 con.close(); } @Test // 2. 【推薦】配置方式實現連接池 , 便于維護 public void testProp() throws Exception { // 加載prop配置文件 Properties prop = new Properties(); // 獲取文件流 InputStream inStream = App_DBCP.class.getResourceAsStream("db.properties"); // 加載屬性配置文件 prop.load(inStream); // 根據prop配置,直接創建數據源對象 DataSource dataSouce = BasicDataSourceFactory.createDataSource(prop); // 獲取連接 Connection con = dataSouce.getConnection(); con.prepareStatement("delete from admin where id=4").executeUpdate(); // 關閉 con.close(); } } |
|
配置方式實現DBCP連接池, 配置文件中的key與BaseDataSouce中的屬性一樣: |
|
db.properties |
|
url=jdbc:mysql:///jdbc_demo driverClassName=com.mysql.jdbc.Driver username=root password=root initialSize=3 maxActive=6 maxIdle=3000 |
注意:這里App_DBCP.class.getResourceAsInputstrem(“/db.properties”);路徑的寫法
帶 / 表示工程的根目錄
不帶/ 表示該類所在的目錄
都是這樣的,/都是表示根
配置文件,一般使用xml和properties文件存儲,前者使用的更為廣泛,后者這是做一些簡單的數據存儲。凡是有關系的尤其是包含關系的,一定是使用xml文件存儲。Properties本事就是一個map影視而已,當然存儲的都是鍵值對。
2.2 C3P0連接池:
C3P0連接池:
最常用的連接池技術!Spring框架,默認支持C3P0連接池技術!
C3P0連接池,核心類:
CombopooledDataSource ds;通過查看源碼,可以發現這個類,也是實現了sun定義DataSource接口的。
使用:
- 下載,引入jar文件: c3p0-0.9.1.2.jar
- 使用連接池,創建連接
a) 硬編碼方式
b) 配置方式(xml),這個xml的模板必須從c3p0的源碼包中拷貝出來,不用更改xml文件的名稱,只需要修改xml文件中的內容即可。創建ComboPooleDataSource對象的時候將會自動加載這個xml配置文件。
Ctrl + shift + \取消注釋,只需要保留<default-config>即可,別的都可以刪除
|
public class App { @Test //1. 硬編碼方式,使用C3P0連接池管理連接 public void testCode() throws Exception { // 創建連接池核心工具類 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 設置連接參數:url、驅動、用戶密碼、初始連接數、最大連接數 dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc_demo"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setUser("root"); dataSource.setPassword("root"); dataSource.setInitialPoolSize(3); dataSource.setMaxPoolSize(6); dataSource.setMaxIdleTime(1000); // ---> 從連接池對象中,獲取連接對象 Connection con = dataSource.getConnection(); // 執行更新 con.prepareStatement("delete from admin where id=7").executeUpdate(); // 關閉 con.close(); } @Test //2. XML配置方式,使用C3P0連接池管理連接 public void testXML() throws Exception { // 創建c3p0連接池核心工具類 // 自動加載src下c3p0的配置文件【c3p0-config.xml】 ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用默認的配置 // 獲取連接 Connection con = dataSource.getConnection(); // 執行更新 con.prepareStatement("delete from admin where id=5").executeUpdate(); // 關閉 con.close(); } } |
2.3 優化
項目,連接的管理,交給連接池!
還是之前的項目:
|
public class JdbcUtil { private static ComboPooledDataSource dataSource = null; /** * 靜態代碼塊準備需要的一些數據 */ static { dataSource = new ComboPooledDataSource(); } /** * 建立數據庫鏈接的操作 */ public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } /** * 關閉數據庫鏈接 * @param conn * @param stmt * @param rs */ public static void closeAll(Connection conn, Statement stmt, ResultSet rs) { if(rs!=null) { try { rs.close(); rs = null; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } if(stmt!=null) { try { stmt.close(); stmt = null; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } if(conn!=null) { try { conn.close(); conn = null; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } } } |
3. 分頁技術
分頁技術:
JSP頁面,用來顯示數據! 如果數據有1000條,分頁顯示,每頁顯示10條,共100頁; 好處: 利于頁面布局,且顯示的效率高!
分頁關鍵點:
- 分頁SQL語句;
- 后臺處理: dao/service/servlet/JSP
分頁的sql語句:
第一個參數:當前頁的其實索引行 = (當前頁數 - 1)*每頁顯示的條目數
第二個參數:每頁顯示的條目數
Select * from employee limit 0, 4;
實現步驟:
- 環境準備
a) 引入jar文件及引入配置文件
- 數據庫驅動包
- C3P0連接池jar文件 及 配置文件
- DbUtis組件: QueryRunner qr = new QueryRuner(dataSouce);
qr.update(sql);
b) 公用類: JdbcUtils.java
- 先設計:PageBean.java
- Dao接口設計/實現: 2個方法
- Service/servlet
- JSP
作業:
需求:自擬
要求功能:
- 列表展示、分頁 【必須有】
- 注冊、登陸
a) 登陸后,進入第1步驟的列表頁面(分頁)
b) 注冊成功,跳轉到登陸頁面,讓用戶登陸
- 擴展
a) 對列表數據修改
b) 對列表數據刪除
c) 新增數據
PageBean的設計
|
public class PageBean <T> { private int currentPage = 1; //當前顯示的頁面 默認顯示第一頁 private int pageCount = 4; //查詢返回的條數 默認 每頁4條數據 private int totalCount; //總記錄數 private int totalPage; //總頁數 private List<T> pageData; //分頁查詢到的數據 public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } public int getPageCount() { return pageCount; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getTotalPage() { if(totalCount % pageCount == 0) { totalPage = totalCount / pageCount; } else { totalPage = totalCount / pageCount + 1; } return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public List<T> getPageData() { return pageData; } public void setPageData(List<T> pageData) { this.pageData = pageData; } } |
分頁項目總的jdbcutil使用了c3p0包和DbUtils包
C3p0是干啥的:創立連接池,從連接池中獲取鏈接的。
DbUtils是干啥的:dbutils下面主要是有兩個類,一個DbUtils類主要是用來實現數據庫的連接的,QueryRunner主要是用來實現執行sql語句的(往往這是我們使用DBUtils的主要內容)
通常是c3p0和DbUtil搭配使用,前者用來獲取連接,后者用來放回一個QueryRunner用來執行sql語句。
|
public class JdbcUtil { private static ComboPooledDataSource dataSource = null; // 初始化 static { // 一旦創建 建輝默認的加載 c3p0文件 dataSource = new ComboPooledDataSource(); } // 創建DbUtils核心工具類對象 public static QueryRunner getQueryRunner() { // 創建 QueryRunner的時候 可以傳入一個參數 datasource 這樣在 執行 qr.executeUpdate()的 //時候 尅不傳入鏈接,這樣最終也不需要 手動的 conn.close() return new QueryRunner(dataSource);// 看到沒有這里都不需要getConnection的操作,所以我們后面也不需要自己師范conn,stmt,rs等資源 } // 當然有可能有某些函數中還需要使用connection所以可以寫一個getConnection // 獲取連接 一遍那些需要使用 數據庫鏈接的地方使用 public static Connection getConnection() { try { return dataSource.getConnection();// 從連接池中 拿出一個鏈接來 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(e); } } } |
<a href="${pageContext.request.contextPath }/index?currentPage=1">首頁</a>這里再說一下,pageContext是jsp中的內置對象,失眠是在EL表達式中使用了內置對象。EL表達式中,使用get方法時只寫get后面的名字就可以了。
一般jsp、html等資源都是給瀏覽器使用。如果使用轉發時,應當寫上完整的路徑,如下:
String uri = “/error/error.jsp”; //這個時候第一個/表示的就是項目根目錄和request.getContextPath()是一樣的。
reqeust.getRequestDispacher(uri).forward(request, response);
如果使用重定向的話如下:
response.sendRedirect(request.getContextPath() + “/error/error.jsp”);
Servlet動態資源,編譯之后,request.getContextPath/class里面,一般servlet資源找給服務器使用,這個時候一般使用的都是重定向request.getRequestDispacher(“/IndexServlet”),直接重定向過去,這個時候,IndexServlet前面的 / 就相當于request.getContextPath
總結
以上是生活随笔為你收集整理的连接池 DBCP c3p0以及分页的案例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 辣什么 探究辣椒的种类和辣度?
- 下一篇: 6、numpy——高级索引