25、連線池(DBCP、C3P0)、動態代理與分頁技術
連線池
思考:
程式中連線如何管理?
1. 連線資源寶貴;需要對連線管理
2. 連線:
a) 運算元據庫,建立連線
b) 操作結束, 關閉!
分析:
涉及頻繁的連線的開啟、關閉,影響程式的執行效率!
連線管理:
預先建立一組連線,有的時候每次取出一個; 用完後,放回;
學習連線池:
a. 自定義一個連線池
b. 學習優秀的連線池元件
a) DBCP
b) C3P0
動態代理
Java提供了一個Proxy類,呼叫它的newInstance方法可以生成某個物件的代理物件,使用該方法生成代理物件時,需要三個引數:
1.生成代理物件使用哪個類裝載器
2.生成哪個物件的代理物件,通過介面指定
3.生成的代理物件的方法裡幹什麼事,由開發人員編寫handler介面的實現來指定。
初學者必須理解,或不理解必須記住的2件事情:
Proxy類負責建立代理物件時,如果指定了handler(處理器),那麼不管使用者呼叫代理物件的什麼方法,該方法都是呼叫處理器的invoke方法(相當於攔截了)。
由於invoke方法被呼叫需要三個引數:代理物件、方法、方法的引數,因此不管代理物件哪個方法呼叫處理器的invoke方法,都必須把自己所在的物件、自己(呼叫invoke方法的方法)、方法的引數傳遞進來。
自定義連線池
代理:
如果對某個介面中的某個指定的方法的功能進行擴充套件,而不想實現介面裡所有方法,可以使用(動態)代理模式!
Java中代理模式:靜態/動態/Cglib代理(spring)
使用動態代理,可以監測介面中方法的執行!
如何對Connection物件,生成一個代理物件
|--Proxy
static Object newProxyInstance(
ClassLoader loader, 當前使用的類載入器
Class<?>[] interfaces, 目標物件(Connection)實現的介面型別
InvocationHandler h 事件處理器:當執行上面介面中的方法的時候,就會自動觸發事件處理器程式碼
把當前執行的方法(method)作為引數傳入。)
/**
* 自定義連線池, 管理連線
* 程式碼實現:
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;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//3. 獲取連線
public Connection getConnection(){
// 3.1 判斷連線池中是否有連線, 如果有連線,就直接從連線池取出
if (pool.size() > 0){
current_count++;
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
}
}
代理的總結:
使用代理,可以在不實現介面的情況,對介面的方法進行擴充套件,新增額外的使用者需要的業務邏輯!
DBCP連線池
概述:
Sun公司約定: 如果是連線池技術,需要實現一個介面!
javax.sql.DataSource;
連線池:
DBCP
C3P0
DBCP連線池:
DBCP 是 Apache 軟體基金組織下的開源連線池實現,使用DBCP資料來源,應用程式應在系統中增加如下兩個 jar 檔案:
• Commons-dbcp.jar:連線池的實現
• Commons-pool.jar:連線池實現的依賴庫
Tomcat 的連線池正是採用該連線池來實現的。該資料庫連線池既可以與應用伺服器整合使用,也可由應用程式獨立使用。
核心類:
BasicDataSource
使用步驟
• 引入jar檔案
commons-dbcp-1.4.jar
commons-pool-1.5.6.jar
配置方式實現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
使用:
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();
}
}
C3P0
C3P0連線池:
最常用的連線池技術!Spring框架,預設支援C3P0連線池技術!
C3P0連線池,核心類:
CombopooledDataSource ds;
使用:
1. 下載,引入jar檔案: c3p0-0.9.1.2.jar
2. 使用連線池,建立連線
a) 硬編碼方式
b) 配置方式(xml)
案例:
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();
}
}
配置方式:
<c3p0-config>
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo
</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/jdbc_demo</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>
重寫Utils
public class JdbcUtils {
/**
* 1. 初始化C3P0連線池
*/
private static DataSource dataSource;
static {
dataSource = new ComboPooledDataSource();
}
/**
* 2. 建立DbUtils核心工具類物件
*/
public static QueryRunner getQueryRuner(){
// 建立QueryRunner物件,傳入連線池物件
// 在建立QueryRunner物件的時候,如果傳入了資料來源物件;
// 那麼在使用QueryRunner物件方法的時候,就不需要傳入連線物件;
// 會自動從資料來源中獲取連線(不用關閉連線)
return new QueryRunner(dataSource);
}
}
分頁技術實踐
分頁技術:
JSP頁面,用來顯示資料! 如果資料有1000條,分頁顯示,每頁顯示10條,共100頁; 好處: 利於頁面佈局,且顯示的效率高!
分頁關鍵點:
- 分頁SQL語句;
- 後臺處理: dao/service/servlet/JSP
實現步驟:
0. 環境準備
a) 引入jar檔案及引入配置檔案
i. 資料庫驅動包
ii. C3P0連線池jar檔案 及 配置檔案
iii. DbUtis元件: QueryRunner qr = new QueryRuner(dataSouce);
qr.update(sql);
b) 公用類: JdbcUtils.java
1. 先設計:PageBean.java
2. Dao介面設計/實現: 2個方法
3. Service/servlet
4. JSP
entity:
public class Employee {
private int empId; // 員工id
private String empName; // 員工名稱
private int dept_id; // 部門id
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int deptId) {
dept_id = deptId;
}
}
Dao:
public interface IEmployeeDao {
/**
* 分頁查詢資料
*/
public void getAll(PageBean<Employee> pb);
/**
* 查詢總記錄數
*/
public int getTotalCount();
}
DaoImpl:
public class EmployeeDao implements IEmployeeDao {
public void getAll(PageBean<Employee> pb) {
//2. 查詢總記錄數; 設定到pb物件中
int totalCount = this.getTotalCount();
pb.setTotalCount(totalCount);
/*
* 問題: jsp頁面,如果當前頁為首頁,再點選上一頁報錯!
* 如果當前頁為末頁,再點下一頁顯示有問題!
* 解決:
* 1. 如果當前頁 <= 0; 當前頁設定當前頁為1;
* 2. 如果當前頁 > 最大頁數; 當前頁設定為最大頁數
*/
// 判斷
if (pb.getCurrentPage() <=0) {
pb.setCurrentPage(1); // 把當前頁設定為1
} else if (pb.getCurrentPage() > pb.getTotalPage()){
pb.setCurrentPage(pb.getTotalPage()); // 把當前頁設定為最大頁數
}
//1. 獲取當前頁: 計算查詢的起始行、返回的行數
int currentPage = pb.getCurrentPage();
int index = (currentPage -1 ) * pb.getPageCount(); // 查詢的起始行
int count = pb.getPageCount(); // 查詢返回的行數
//3. 分頁查詢資料; 把查詢到的資料設定到pb物件中
String sql = "select * from employee limit ?,?";
try {
// 得到Queryrunner物件
QueryRunner qr = JdbcUtils.getQueryRuner();
// 根據當前頁,查詢當前頁資料(一頁資料)
List<Employee> pageData = qr.query(sql, new BeanListHandler<Employee>(Employee.class), index, count);
// 設定到pb物件中
pb.setPageData(pageData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
service:
public interface IEmployeeService {
/**
* 分頁查詢資料
*/
public void getAll(PageBean<Employee> pb);
}
serviceimpl:
public class EmployeeService implements IEmployeeService {
// 建立Dao例項
private IEmployeeDao employeeDao = new EmployeeDao();
public void getAll(PageBean<Employee> pb) {
try {
employeeDao.getAll(pb);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
servlet:
public class IndexServlet extends HttpServlet {
// 建立Service例項
private IEmployeeService employeeService = new EmployeeService();
// 跳轉資源
private String uri;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
//1. 獲取“當前頁”引數; (第一次訪問當前頁為null)
String currPage = request.getParameter("currentPage");
// 判斷
if (currPage == null || "".equals(currPage.trim())){
currPage = "1"; // 第一次訪問,設定當前頁為1;
}
// 轉換
int currentPage = Integer.parseInt(currPage);
//2. 建立PageBean物件,設定當前頁引數; 傳入service方法引數
PageBean<Employee> pageBean = new PageBean<Employee>();
pageBean.setCurrentPage(currentPage);
//3. 呼叫service
employeeService.getAll(pageBean); // 【pageBean已經被dao填充了資料】
//4. 儲存pageBean物件,到request域中
request.setAttribute("pageBean", pageBean);
//5. 跳轉
uri = "/WEB-INF/list.jsp";
} catch (Exception e) {
e.printStackTrace(); // 測試使用
// 出現錯誤,跳轉到錯誤頁面;給使用者友好提示
uri = "/error/error.jsp";
}
request.getRequestDispatcher(uri).forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
PageBean:
public class PageBean<T> {
private int currentPage = 1; // 當前頁, 預設顯示第一頁
private int pageCount = 4; // 每頁顯示的行數(查詢返回的行數), 預設每頁顯示4行
private int totalCount; // 總記錄數
private int totalPage; // 總頁數 = 總記錄數 / 每頁顯示的行數 (+ 1)
private List<T> pageData; // 分頁查詢到的資料
// 返回總頁數
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 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 List<T> getPageData() {
return pageData;
}
public void setPageData(List<T> pageData) {
this.pageData = pageData;
}
}
jsp:
<html>
<head>
<title>分頁查詢資料</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<table border="1" width="80%" align="center" cellpadding="5" cellspacing="0">
<tr>
<td>序號</td>
<td>員工編號</td>
<td>員工姓名</td>
</tr>
<!-- 迭代資料 -->
<c:choose>
<c:when test="${not empty requestScope.pageBean.pageData}">
<c:forEach var="emp" items="${requestScope.pageBean.pageData}" varStatus="vs">
<tr>
<td>${vs.count }</td>
<td>${emp.empId }</td>
<td>${emp.empName }</td>
</tr>
</c:forEach>
</c:when>
<c:otherwise>
<tr>
<td colspan="3">對不起,沒有你要找的資料</td>
</tr>
</c:otherwise>
</c:choose>
<tr>
<td colspan="3" align="center">
當前${requestScope.pageBean.currentPage }/${requestScope.pageBean.totalPage }頁
<a href="${pageContext.request.contextPath }/index?currentPage=1">首頁</a>
<a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.currentPage-1}">上一頁 </a>
<a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.currentPage+1}">下一頁 </a>
<a href="${pageContext.request.contextPath }/index?currentPage=${requestScope.pageBean.totalPage}">末頁</a>
</td>
</tr>
</table>
</body>
</html>
相關文章
- 解密httpclient,dbcp,jedis,c3p0,druid,okhttp都在使用的連線池技術解密HTTPclientUI
- DBCP連線池原理分析
- 資料庫連線池優化配置(druid,dbcp,c3p0)資料庫優化UI
- 常用資料庫連線池 (DBCP、c3p0、Druid) 配置說明資料庫UI
- DBCP連線池配置引數說明
- java資料庫連線池dbcp的使用Java資料庫
- DBCP 資料連線池的配置和使用
- 使Domino支援連線池技術
- 用idea配置c3p0連線池Idea
- Hibernate使用C3P0的連線池
- Java中的資料庫連線池:HikariCP與C3P0Java資料庫
- 走進JavaWeb技術世界3:JDBC的進化與連線池技術JavaWebJDBC
- 從原始碼分析DBCP資料庫連線池的原理原始碼資料庫
- JDBC【資料庫連線池、DbUtils框架、分頁】JDBC資料庫框架
- c3p0資料庫連線池問題資料庫
- 資料庫連線池技術詳解資料庫
- MySQL第七天----Connection連線池、包裝模式(裝飾模式)與動態代理模式MySql模式
- Java中的超程式設計與動態代理技術Java程式設計
- mybatis動態sql與分頁MyBatisSQL
- tomcat自帶連線池dbcp配置以及最佳化說明Tomcat
- 求教怎樣使用commons-dbcp元件的連線池問題元件
- 4、資料庫連線池的概念及C3P0、Uruid兩種連線池的使用資料庫UI
- Spring DataSource>DBCP & C3P0Spring
- jsoup爬蟲技術+druid連線池JS爬蟲UI
- Win32動態連線庫基址重置技術 (轉)Win32
- 資料庫連線(2) - 為什麼C3P0連線池那麼慢資料庫
- Http持久連線與HttpClient連線池HTTPclient
- Jedis連線池究竟是何物|得物技術
- python資料庫連線池技術總結Python資料庫
- 技術分享:Proxy-Pool代理池搭建IP代理
- SSH 框架整合(maven版本 xml配置方式 連線池c3p0)框架MavenXML
- 關於hibernate使用c3p0連線池的問題.
- Tomcat DBCP(Database Connection Pool) 資料庫連線池入門介紹TomcatDatabase資料庫
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- 看完Java的動態代理技術——Pythoner笑了JavaPython
- Java技術分享:什麼是資料庫連線池?Java資料庫
- Java GenericObjectPool 物件池化技術--SpringBoot sftp 連線池工具類JavaObject物件Spring BootFTP
- Java篇-DBUtils與連線池Java