如何更新網(wǎng)站緩存濟南網(wǎng)絡優(yōu)化廠家
本文全面的介紹了JDBC的相關知識,包括其基礎與高級應用、Service層的事務管理、ThreadLocal(本地線程變量)、 數(shù)據(jù)庫連接池、Commons Dbutils的原理及其使用!
文章目錄
- 一、JDBC基礎
- 1.1JDBC介紹
- 1.2JDBC的核心類與接口
- 1.java.sql.DriverManager類 (`驅(qū)動管理器`)
- 2.java.sql.Connection接? (`數(shù)據(jù)庫連接`)
- 3.java.sql.Connection接? (`數(shù)據(jù)庫連接`)
- 4.java.sql.ResultSet接? (`結(jié)果集`)
- 1.3SQL注?問題
- 1.4 JDBC開發(fā)步驟
- 二、JDBC高級應用
- 2.1 三層架構與面向結(jié)構編程
- 2.1 封裝
- 2.2 Service層的事務管理
- 2.3 ThreadLocal(本地線程變量)
- 2.4 數(shù)據(jù)庫連接池
- 2.5 Commons Dbutils(Apache提供的dao層工具類)
- (1)原理(自定義DaoUtils實現(xiàn)通用)
- (2)Apache的DbUtils
一、JDBC基礎
1.1JDBC介紹
- JDBC (全稱Java DataBase Contectivity) :Java與數(shù)據(jù)庫的連接,數(shù)據(jù)庫編程。
- JDBC 是Java語?(JDK)為完成數(shù)據(jù)庫的訪問操作提供的?套統(tǒng)?的標準
- 驅(qū)動包下載: 下載驅(qū)動jar包,下載地址:https://mvnrepository.com/,打開?址搜索 mysql 。
- (開發(fā)者通過JDK提供的規(guī)范與數(shù)據(jù)庫廠商提供的驅(qū)動,將驅(qū)動類加載到程序中使用,進而達到通過程序操作數(shù)據(jù)庫)
1.2JDBC的核心類與接口
1.java.sql.DriverManager類 (
驅(qū)動管理器
)
- 注冊驅(qū)動
- 創(chuàng)建數(shù)據(jù)庫連接
(1)注冊驅(qū)動
- 在Driver類中的靜態(tài)初始化塊中,注冊驅(qū)動:DriverManager.registerDriver(new
Driver());public class Driver extends NonRegisteringDriver implements >java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}} }
- 在我們的應?程序中?動注冊驅(qū)動的代碼也可以省略
【Class.forName(“com.mysql.cj.jdbc.Driver”);】
如果我們沒有?動注冊驅(qū)動,驅(qū)動管理器在獲取連接的時候發(fā)現(xiàn)沒有注冊驅(qū)動則讀取 驅(qū)動jar/META-INF/servicesjava.sql.Driver?件中配置的驅(qū)動類路徑進?注冊
不推薦
(2)獲取連接
// url 數(shù)據(jù)庫服務器的地址 // username 數(shù)據(jù)庫連接??名 // password 數(shù)據(jù)庫連接密碼 Connection connection = DriverManager.getConnection(url, "root","123456");
2.java.sql.Connection接? (
數(shù)據(jù)庫連接
)Connection對象表?Java應?程序與數(shù)據(jù)庫之間的連接
- 通過Connection接?對象,獲取執(zhí)?SQL語句的Statement對象
- 完成數(shù)據(jù)的事務管理
(1) 獲取Statement對象
- Statement接?: 編譯執(zhí)?靜態(tài)SQL指令
Statement statement = connection.createStatement();
- PreparedStatement接?:繼承了Statement接?,
預編譯動態(tài)SQL指令(解決SQL注?問題)
PreparedStatement preparedStatement = connection.prepareStatement(sql);
- CallableStatement接?:繼承了PreparedStatement接?,可以調(diào)?存儲過程
CallableStatement callableStatement = connection.prepareCall(sql);
(2)事務管理
//開啟事務(關閉事務?動提交) connection.setAutoCommit(false); //事務回滾 connection.rollback(); //提交事務 connection.commit();
3.java.sql.Connection接? (
數(shù)據(jù)庫連接
)—用于編譯、執(zhí)行SQL指令
// 執(zhí)?DML操作的SQL指令(返回值是受影響行數(shù)) int row = statement.executeUpdate(sql); // 執(zhí)?DQL操作的SQL指令(返回結(jié)果集) ResultSet rs = statement.executeQuery(sql);
4.java.sql.ResultSet接? (
結(jié)果集
)— ResultSet接?對象,表?查詢操作返回的結(jié)果集,提供了便利的?法?于獲取結(jié)果集中的數(shù)據(jù)
//res.next()用于判斷下一個位置是否還有值,初始時位于首元素之前while (res.next()){//res.getInt("tid"):通過數(shù)據(jù)庫字段名獲取數(shù)據(jù)//res.getInt(2):通過字段列標獲取數(shù)據(jù)(列標從1開始)teacher=new Teacher(res.getInt("tid"),res.getString("tname"),res.getString("gender"),res.getDate("workingdate"),res.getString("workgrade"));}
1.3SQL注?問題
(1)什么是SQL注?問題
- 在JDBC操作SQL指令編寫過程中,如果SQL指令中需要數(shù)據(jù),我們可以通過字符串拼接的形式將參數(shù)拼接到SQL指令中,如
String sql = "delete from books where book_id="+s;
(s就是拼接到SQL中的變量)- 使?字符串拼接變量的形式來設置SQL語句中的數(shù)據(jù),可能會導致
因變量值的改變引起SQL指令的原意發(fā)?改變
,這就被稱為SQL注?。SQL注?問題是需要避免的- 例如:
如果s的值為1
,SQL指令 : delete from books where book_id=1,
如果s的值為1 or 1=1
,SQL指令:delete from books where book_id=1 or 1=1, 那么SQL中的條件則是?個恒等式(sql指令發(fā)生變化)
(2)如何解決SQL注?問題
使?PreparedStatement進?SQL預編譯解決SQL注?問題:
- 在編寫SQL指令時,如果SQL指令中需要參數(shù),?律使?
?
參數(shù)占位符- 如果SQL指令中有
?
,在JDBC操作步驟中不再使?Statement,?是從Conection對象獲取PreparedStatement對SQL指令進?預編譯PreparedStatement preparedStatement = connection.prepareStatement(sql);
- 預編譯完成之后,通過PreparedStatement對象給預編譯后的SQL指令的
?
賦值
- prepareadStatement.setInt(參數(shù)占位符序號,值);
- prepareadStatement.setString(參數(shù)占位符序號,值);
- SQL指令中的所有
?
完成賦值之后,通過PreparedStatement執(zhí)?SQL執(zhí)?SQL時不再加載SQL語句
- int row = prepareadStatement.executeUpdate();
- ResultSet rs = preparedStatement.executeQuery();
1.4 JDBC開發(fā)步驟
- 加載驅(qū)動
// 1.加載驅(qū)動類
Class.forName("com.mysql.jdbc.Driver");
- 創(chuàng)建連接
//2.建立連接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/javaTest", "root", "123456");
- 編寫sql指令
//3.編寫sql語句String sql="insert into student values(2,'李四')";
- 創(chuàng)建執(zhí)行體
//4.創(chuàng)建執(zhí)行體stmt = conn.createStatement();
- 執(zhí)行sql指令
//5.執(zhí)行sql語句int row = stmt.executeUpdate(sql);
- 處理執(zhí)行結(jié)果(集)
//6.處理結(jié)果if (row>-1){System.out.println("插入成功!");}else{System.out.println("插入失敗!");}
- 釋放占用資源
//釋放資源try {stmt.close();conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}
二、JDBC高級應用
2.1 三層架構與面向結(jié)構編程
(1)三層架構
三層架構是指:視圖層 View、服務層 Service,與持久層 Dao。它們分別完成不同的功能。
- View 層:用于接收用戶提交請求。
- Service 層:用以實現(xiàn)系統(tǒng)的業(yè)務邏輯
- Dao 層:用以實現(xiàn)直接對數(shù)據(jù)庫進行操作。
(2)面向接口 (抽象) 編程
面向接口編程:程序設計時,考慮易修改、易擴展,為Service層和DAO層設計接口,便于未來更換實現(xiàn)類
在三層架構程序設計中,采用面向接口(抽象)編程。
- 實現(xiàn)方式
- 上層對下層的調(diào)用,是通過下層接口實現(xiàn)的
- 而下層對上層的真正服務提供者,是下層接口的實現(xiàn)類
- 特點:
- 服務標準(接口:規(guī)范相同)是
相同
的,服務提供者(實現(xiàn)類)可以更換
。這就實現(xiàn)了層間解耦合與編程的靈活性
2.1 封裝
(1)DAO封裝 — (DAO Data Access Object 數(shù)據(jù)訪問對象)
將對數(shù)據(jù)庫中同?張數(shù)據(jù)表的JDBC操作?法封裝到同?個Java類中,這個類就是訪問此數(shù)據(jù)表的
數(shù)據(jù)訪問對象
(2)DTO封裝 — ( Data Transfer Object 數(shù)據(jù)傳輸對象(實體類))
在Java程序中創(chuàng)建?個屬性與數(shù)據(jù)庫表匹配的類,通過此類的對象封裝查詢到的數(shù)據(jù),我們把?于傳遞JDBC增刪查改操作的數(shù)據(jù)的對象稱之為
數(shù)據(jù)傳輸對象
2.2 Service層的事務管理
(1)事務的概念
事務是指是程序中一系列嚴密的邏輯操作,而且所有操作必須全部成功完成,否則在每個操作中所作的所有更改都會被撤消。
(2)事務的四大特性
- 原子性(Atomicity):操作這些指令時,要么全部執(zhí)行成功,要么全部不執(zhí)行。只要其中一個指令執(zhí)行失敗,所有的指令都執(zhí)行失敗,數(shù)據(jù)進行回滾,回到執(zhí)行指令前的數(shù)據(jù)狀態(tài)。
- 一致性(Consistency): 事務的執(zhí)行使數(shù)據(jù)從一個狀態(tài)轉(zhuǎn)換為另一個狀態(tài),但是對于整個數(shù)據(jù)的完整性保持穩(wěn)定。
- 隔離性(Isolation): 隔離性是當多個用戶并發(fā)訪問數(shù)據(jù)庫時,比如操作同一張表時,數(shù)據(jù)庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,
多個并發(fā)事務之間要相互隔離。
- 持久性(Durability): 當事務正確完成后,它對于數(shù)據(jù)的改變是永久性的。
(3)事務的隔離級別
第一種隔離級別:Read uncommitted(讀未提交)
如果一個事務已經(jīng)開始寫數(shù)據(jù),則另外一個事務不允許同時進行寫操作,但允許其他事務讀此行數(shù)據(jù),該隔離級別可以通過“排他寫鎖”,但是不排斥讀線程實現(xiàn)。這樣就避免了更新丟失,卻可能出現(xiàn)臟讀,也就是說事務B讀取到了事務A未提交的數(shù)據(jù)
解決了更新丟失,但還是可能會出現(xiàn)臟讀
第二種隔離級別:Read committed(讀提交)
如果是一個讀事務(線程),則允許其他事務讀寫,如果是寫事務將會禁止其他事務訪問該行數(shù)據(jù),該隔離級別避免了臟讀,但是可能出現(xiàn)不可重復讀。事務A事先讀取了數(shù)據(jù),事務B緊接著更新了數(shù)據(jù),并提交了事務,而事務A再次讀取該數(shù)據(jù)時,數(shù)據(jù)已經(jīng)發(fā)生了改變。
解決了更新丟失和臟讀問題
第三種隔離級別:Repeatable read(可重復讀取)
可重復讀取是指
在一個事務內(nèi)
,多次讀同一個數(shù)據(jù),在這個事務還沒結(jié)束時,其他事務不能訪問該數(shù)據(jù)(包括了讀寫),這樣就可以在同一個事務內(nèi)兩次讀到的數(shù)據(jù)是一樣的,因此稱為是可重復讀隔離級別,讀取數(shù)據(jù)的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務(包括了讀寫)
,這樣避免了不可重復讀和臟讀,但是有時可能會出現(xiàn)幻讀。(讀取數(shù)據(jù)的事務)可以通過“共享讀鏡”和“排他寫鎖”實現(xiàn)。解決了更新丟失、臟讀、不可重復讀、但是還會出現(xiàn)幻讀
第四種隔離級別:Serializable(序列化)
提供嚴格的事務隔離,它要求事務序列化執(zhí)行,事務只能一個接著一個地執(zhí)行,但不能并發(fā)執(zhí)行,如果僅僅通過“行級鎖”是無法實現(xiàn)序列化的,必須通過其他機制保證新插入的數(shù)據(jù)不會被執(zhí)行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也是最高的,性能很低,一般很少使用,在該級別下,事務順序執(zhí)行,不僅可以避免臟讀、不可重復讀,還避免了幻讀
解決了更新丟失、臟讀、不可重復讀、幻讀(虛讀)
隔離級別 臟讀 不可重復度 幻讀 Read uncommitted(讀未提交) √ √ √ Read committed(讀提交) × √ √ Repeatable read(可重復讀取) × × √ Serializable(序列化) × × ×
- 臟讀
- 所謂臟讀是指一個事務中訪問到了另外一個事務未提交的數(shù)據(jù)
- 幻讀
- 一個事務讀取2次,得到的記錄條數(shù)不一致
- 不可重復讀
- 一個事務讀取同一條記錄2次,得到的結(jié)果不一致
MySQL事務管理:
- start transaction (開啟事務)
- end transaction (結(jié)束事務)
- rollback (事務回滾)
- commit (提交事務)
(4)JDBC事務管理
- ?個事務中的多個DML操作需要
基于同?個數(shù)據(jù)庫連接
- 創(chuàng)建連接之后,設置事務?動提交(關閉?動提交connection.setAutoCommit(false);
- 當當前事務中的
所有DML操作
完成之后?動提交 connection.commit();- 當事務中的任何?個步驟出現(xiàn)異常,在catch代碼塊中執(zhí)?事務回滾
connection.rollback();
(5)Service層簡介
DAO負責特定的數(shù)據(jù)庫操作,業(yè)務由service層進?管理
- 業(yè)務:指的是完成某一功能(軟件提供的一個功能)
- 例如:A給B轉(zhuǎn)帳(其包含A賬戶減錢,B賬戶加錢),其整體為一個業(yè)務的操作
- Servcie進?業(yè)務處理,Service業(yè)務處理過程如果需要數(shù)據(jù)庫操作,則調(diào)?DAO完成
- Service層的一個業(yè)務,可能需要調(diào)用一個或若干個DAO層對數(shù)據(jù)庫進行處理
(6)Service層事務管理
事務管理要滿?以下條件:
- 多個DML操作需使?同?個數(shù)據(jù)庫連接
- 第?個DML操作之前設置事務?動提交
- 所有DML操作執(zhí)?完成之后提交事務
- 出現(xiàn)異常則進?事務回滾
1.需要解決的問題
- Servcie層事務可能涉及多個DAO層,其中多個數(shù)據(jù)庫的DML操作是相互獨?的,如何保證所有DML要么同時成功,要么同時失敗呢?
2.解決辦法:
讓Service事務中的多個DML使?同?個數(shù)據(jù)庫連接
方式一:在Service獲取連接對象,將連接對象傳遞到DAO中
- 分析: DAO類中的Connection對象需要通過Service傳遞給進來,這種對象傳遞本來也?可厚?,但是當我們通過?向接?開發(fā)時(
?向接?,是為了能夠靈活的定義實現(xiàn)類
),容易造成接?的冗余(接?污染) - 接口污染典型示例: 不同的數(shù)據(jù)庫的數(shù)據(jù)庫連接對象是不同的,MySQL的連接對象是
Connection
但Oracle數(shù)據(jù)庫則不是
方式二:使?ThreadLocal容器,實現(xiàn)多個DML操作使?相同的連接
- 不使用自定義List集合的原因:
- 存儲Connection的容器可以使?List集合,使?List集合做容器,在多線程并發(fā)編程中會出現(xiàn)資源競爭問題,多個并發(fā)的線程使?的是同?個數(shù)據(jù)庫連接對象(
我們的要求是同?個事務中使?同?個連接,?并?多個線程共享同一個連接
) - 為了解決并發(fā)編程的連接對象共享問題,我們可以
使?ThreadLocal作為數(shù)據(jù)庫連接對象的容器
- 存儲Connection的容器可以使?List集合,使?List集合做容器,在多線程并發(fā)編程中會出現(xiàn)資源競爭問題,多個并發(fā)的線程使?的是同?個數(shù)據(jù)庫連接對象(
2.3 ThreadLocal(本地線程變量)
(1)ThreadLocal簡介
ThreadLocal
叫做本地線程變量,意思是說,ThreadLocal
中填充的的是當前線程的變量,該變量對其他線程而言是封閉且隔離的,ThreadLocal
為變量在每個線程中創(chuàng)建了一個副本,這樣每個線程都可以訪問自己內(nèi)部的副本變量。
(2)ThreadLocal的應用
- 在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
- 線程間數(shù)據(jù)隔離
- 進行事務操作,用于存儲線程事務信息。
- 數(shù)據(jù)庫連接,
Session
會話管理。
(3)ThreadLocal常用的方法
set()方法:
ThreadLocal對象.set()
會為ThreadLocal對象調(diào)用set()方法所在的線程中的ThreadLocal.ThreadLocalMap threadLocals = null;
進行賦值,賦值類型為一個Map,Map的鍵為當前的ThreadLocal對象,值為所傳入的值.set()的源碼
public void set(T value) { //獲取當前ThreadLocal對象所在的線程 Thread t = Thread.currentThread(); //獲取所在線程中存儲的threadLocals(ThreadLocalMap) ThreadLocalMap map = getMap(t); //判斷map是否為空 if (map != null)//不為空,則替換值map.set(this, value); else//為空則為當前線程創(chuàng)建ThreadLocalMap對象并為threadLocals賦值createMap(t, value); }void createMap(Thread t, T firstValue) { //為線程t中的threadLocals創(chuàng)建一個ThreadLocalMap對象進行賦值 t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap
為ThreadLocal
的一個靜態(tài)內(nèi)部類,里面定義了Entry
來保存數(shù)據(jù)。而且是繼承的弱引用。在Entry
內(nèi)部使用ThreadLocal
作為key
,使用我們設置的value
作為value
。對于每個線程內(nèi)部有個
ThreadLocal.ThreadLocalMap
變量,存取值的時候,也是從這個容器中來獲取。
- get()方法
public T get() {//獲取當前ThreadLocal對象所在的線程Thread t = Thread.currentThread();//獲取所在線程中存儲的threadLocals(ThreadLocalMap)ThreadLocalMap map = getMap(t);//判斷線程中存儲的ThreadLocalMap是否為空if (map != null) {//不為空,則以當前ThreadLocal對象為鍵獲取Entry對象ThreadLocalMap.Entry e = map.getEntry(this);//判斷Entry對象是個為空if (e != null) {//不為空則返回Entry對象的值@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//線程中ThreadLocalMap為空或者未存儲以當前ThreadLocal對象為鍵的Entry對象時設置初始值return setInitialValue(); }//設置為線程中ThreadLocalMap的初始值 private T setInitialValue() {//設置當前值為nullT value = initialValue();//獲取當前ThreadLocal對象所在的線程Thread t = Thread.currentThread();//獲取所在線程中存儲的threadLocals(ThreadLocalMap)ThreadLocalMap map = getMap(t);//判讀map是否為空if (map != null)//map不為空向map中添加一個以當前ThreadLocal對象為鍵。值為空的Entry對象map.set(this, value);else//map為空則為當前線程的threadLocals進行創(chuàng)建對象初始化createMap(t, value);return value; }protected T initialValue() {return null; }
(4)ThreadLocal的內(nèi)存泄流問題
內(nèi)存泄漏原因
? 當ThreadLocal為null時,也就是要被垃圾回收器回收了,但是此時我們的ThreadLocalMap(thread 的內(nèi)部屬性)生命周期和Thread的一樣,它不會回收,這時候就出現(xiàn)了一個現(xiàn)象。那就是ThreadLocalMap的key沒了,但是value還在,這就造成了內(nèi)存泄漏。解決方法:
? 用完ThreadLocal
后,執(zhí)行remove
操作,避免出現(xiàn)內(nèi)存溢出情況。所以 如同lock
的操作 最后要執(zhí)行解鎖操作一樣,ThreadLocal
使用完畢一定記得執(zhí)行remove
方法,清除當前線程的數(shù)值。如果不remove
當前線程對應的VALUE
,就會一直存在這個值。
(5)強引用,弱引用,軟引用
- 強引用:普通的引用,強引用指向的對象不會被回收;
- 軟引用:僅有軟引用指向的對象,只有發(fā)生gc且內(nèi)存不足,才會被回收;
- 弱引用:僅有弱引用指向的對象,只要發(fā)生gc就會被回收。
(6)多線程環(huán)境下ThreadLocal在事務中操作可能出現(xiàn)的問題
在闡述此問題時,需要簡要介紹一下mysql數(shù)據(jù)庫的事務默認隔離級別(Repeatable read(可重復讀取))
- 第三種隔離級別:Repeatable read(可重復讀取)
可重復讀取是指在一個事務內(nèi)
,多次讀同一個數(shù)據(jù),在這個事務還沒結(jié)束時,其他事務不能訪問該數(shù)據(jù)(包括了讀寫),這樣就可以在同一個事務內(nèi)兩次讀到的數(shù)據(jù)是一樣的,因此稱為是可重復讀隔離級別,讀取數(shù)據(jù)的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務(包括了讀寫)
,這樣避免了不可重復讀和臟讀,但是有時可能會出現(xiàn)幻讀。
- 場景
- 存在兩個線程,因為ThreadLocal保存數(shù)據(jù)庫連接變量,可以保證兩個線程各自擁有自己的數(shù)據(jù)庫連接,一般在操作各自線程任務的事務時不會出現(xiàn)沖突和干擾
- 現(xiàn)在有如下場景,兩個線程同時操作同一個數(shù)據(jù)中的同一張表,a線程使1號用戶給3號用戶轉(zhuǎn)賬,b線程使1號用戶給2號客戶轉(zhuǎn)賬,a、b線程同時運行搶奪cpu的執(zhí)行權,
此時程序中只會有一個線程執(zhí)行成功,另一個線程執(zhí)行失敗
(原因:mysql默認為Repeatable read(可重復讀取)隔離級別,ab兩線程同時操作寫,違反了mysql的事務隔離級別)
- 根本原因:每個線程操作的數(shù)據(jù)庫中
表的副本
,在對數(shù)據(jù)庫的表操作過程中會有以下驗證:當a線程拿到表的副本后,會記錄拿到副本當時表內(nèi)容狀態(tài)T
,當它修改完自己拿到的副本后準備提交給數(shù)據(jù)庫時,會將自己記錄的表狀態(tài)T
與提交前數(shù)據(jù)庫中此表狀態(tài)進行比對,如果二者不一致,則
線程a不會提交自己的副本給數(shù)據(jù)庫,并會報錯(比對結(jié)果,表明在a線程修改表內(nèi)容的期間,有其他線程對表進行了更改,所以會報錯
)
2.4 數(shù)據(jù)庫連接池
1.引入數(shù)據(jù)庫連接池的原因
- 如果每個JDBC操作需要數(shù)據(jù)庫連接都重新創(chuàng)建,使?完成之后都銷毀,我們的JVM會因為頻繁的創(chuàng)建、銷毀連接?占?額外的系統(tǒng)資源。
- 數(shù)據(jù)庫連接本質(zhì)上是可被重?的資源(當?個JDBC操作完成之后,其創(chuàng)建的連接是可以被其他JDBC操作使?的),基于這個特性:
- 我們可以創(chuàng)建?個 存放數(shù)據(jù)庫連接的容器 (連接池),連接池是有最?容量的
- 當我們要進?JDBC操作時,直接從這個容器中獲取連接:
- 如果容器中沒有空閑的連接且連接池中連接的個數(shù)沒有達到最?值,則創(chuàng)建新的數(shù)據(jù)庫連接存?連接池并給這個操作使?,使?完成之后?需關閉連接直接歸還這個容器中即可
- 如果容器中沒有空閑的連接且連接池中連接的個數(shù)達到最?值,當前操作就會進?等待,等待連接池中的某個連接被歸還,歸還之后再使?
- 如果容器中有空閑連接,則?需創(chuàng)建新的連接,直接從容器中獲取這個空閑連接進?使?
2.概念
- 連接池:存放數(shù)據(jù)庫連接對象的容器
- 連接池作?:對數(shù)據(jù)庫連接進?管理,減少因重復創(chuàng)建、銷毀連接導致的系統(tǒng)開銷
3.常用的線程池
功能 | dbcp | druid | c3p0 | tomcat-jdbc | HikariCP |
---|---|---|---|---|---|
是否支持PSCache | 是 | 是 | 是 | 否 | 否 |
監(jiān)控 | jmx | jmx/log/http | jmx,log | jmx | jmx |
擴展性 | 弱 | 好 | 弱 | 弱 | 弱 |
sql攔截及解析 | 無 | 支持 | 無 | 無 | 無 |
代碼 | 簡單 | 中等 | 復雜 | 簡單 | 簡單 |
特點 | 依賴于common-pool | 阿里開源,功能全面 | 歷史久遠,代碼邏輯復雜,且不易維護 | 優(yōu)化力度大,功能簡單,起源于boneCP | |
連接池管理 | LinkedBlockingDeque | 數(shù)組 | FairBlockingQueue | threadlocal+CopyOnWriteArrayList |
- 由于boneCP被hikariCP替代,并且已經(jīng)不再更新,boneCP沒有進行調(diào)研。
- proxool網(wǎng)上有評測說在并發(fā)較高的情況下會出錯,proxool便沒有進行調(diào)研。
- druid的功能比較全面,且擴展性較好,比較方便對jdbc接口進行監(jiān)控跟蹤等。
- c3p0歷史悠久,代碼及其復雜,不利于維護。并且存在deadlock的潛在風險。
- 基于連接池的性能、使?的便捷性、連接監(jiān)控等多??綜合情況,druid是?前企業(yè)應?中使?最 ?泛的
- Hikari在SpringBoot中默認集成,性能是諸多競品中最好的
4.Druid線程池的使用
- 參考我的另一篇博客: Druid連接池簡介及其使用
2.5 Commons Dbutils(Apache提供的dao層工具類)
(1)原理(自定義DaoUtils實現(xiàn)通用)
- 核心思想:使用泛型
- 參考我的另一篇博客: DaoUtils實現(xiàn)通用(增、刪、改、查)
(2)Apache的DbUtils
- 核心思想:反射
- 注意事項:創(chuàng)建QueryRunner對象時,不能使用無參構造方法,需要傳入一個連接池對象(配合線程池使用)
- 參考我的另一篇博客: DaoUtils實現(xiàn)通用(增、刪、改、查)