從零開始學後端(4)——JDBC的重構設計
重構(Refactoring)就是通過調整程式程式碼,改善軟體的質量、效能,使其程式的設計模式和架構更趨合理,提高軟體的擴充套件性和維護性。
問題1:每個DAO方法中都會寫:驅動名稱/url/賬號/密碼,不利於維護.
如果現在我們從MySQL遷移到Oracle中去,此時就得修改每一個DAO方法的驅動名稱/url/賬號/密碼.
解決方案:使用成員變數來表示,成員變數的作用域在整個類中有效.
問題1已經解決(通過在DAO類中定義成員變數).
問題2:我們在開放中會存在N個DAO實現類,那麼此時每一個DAO實現類中都得提供連線資料庫的四個基本要素的成員變數.
解決方案:現在需要完成在多個類中共享驅動名稱/url/賬號/密碼四個資訊,我們可以定義一個類(JdbcUtil),把這四個資訊儲存在該類中,並且使用public static修飾.
此時DAO程式碼:
問題3: 問題2是完美解決了,但是存在一些遺憾:
1):JdbcUtil中的四個欄位應該私有化起來,體現封裝.
2):其實在DAO實現類中,僅僅需要獲取一個Connection物件即可,至於該物件如何建立,可以不關心.
解決方案:我們在JdbcUtil類中提供一個靜態方法,用於返回Connection物件.
此時DAO程式碼:
conn = JdbcUtil.getConn();
問題4:我們分析右圖JdbcUtil類中的getConn方法的程式碼,發現,每次呼叫getConn方法都需要載入註冊驅動,而我們其實就只需要在最初載入一次即可.
解決方案:在靜態程式碼塊中去載入和註冊資料庫驅動即可.
問題5:每一個DAO方法最後,都需要釋放資源,該程式碼沒有技術含量,又臭又長.
DML操作: 關閉Connection和Statement物件.
DQL操作: 關閉Connection和Statement以及ResultSet物件.
解決方案:在JdbcUtil類中提供close方法用於關閉三個資源物件.
關閉DML操作資源:JdbcUtil.close(conn,st,null);
關閉DQL操作資源: JdbcUtil.close(conn,st,rs);
問題6:在JdbcUtil類中提供了四個欄位分別表示連線資料庫的四要素(驅動類名,URL,賬號,密碼),存在硬編碼,如果需要修改連線的資料庫,就只能來修改該原始碼.
解決方案: 我們一般把資料庫的連線資訊存放到屬性檔案中(db.properties).
接下來再通過Properties類來載入資原始檔,並讀取其中的資訊即可.
問題7:在DAO方法中拼接SQL語句,很噁心,稍後使用PreparedStatement解決.
問題8:在每一個DAO方法中都建立一個新的Connection物件,使用之後,就立刻釋放了,也就是說沒有充分利用Connection物件,而建立Connection物件的成本非常大,
問題9:DML操作模板是相同的,DQL操作模板也是相同的.
預編譯語句物件
預編譯語句物件 VS 靜態語句物件
Statement和PreparedStatement的區別:
PreparedStatement 的優點:
1).PreparedStatement 程式碼的可讀性和可維護性. (SQL模板,使用佔位符表示引數)
2).PreparedStatement 能最大可能提高效能(預編譯),MySQL不支援PreparedStatement的效能優化.
3).PreparedStatement 能保證安全性.
可以防止SQL注入:演示登陸操作
選擇:使用PreparedStatement.
事務管理操作
案例:銀行轉賬:從張無忌賬戶上給趙敏轉1000塊.
準備:account(賬戶表):
id name(賬號,唯一) balance(餘額)
1 張無忌 20000
2 趙敏 0
操作步驟:
1):檢查張無忌的賬戶餘額是否大於等於1000.
SELECT * FROM account WHERE name = ‘張無忌’ AND balance >= 1000;
2):從張無忌的賬戶餘額中減少1000.
UPDATE account SET balance = balance - 1000 WHERE name = ‘張無忌’;
3):再在趙敏的賬戶餘額中增加1000.
UPDATE account SET balance = balance + 1000 WHERE name = ’ 趙敏’;
如果:在第二步和第三步之間如果程式中斷,怎麼辦? 通過異常來模擬.
事務(Transaction,簡寫為tx):
在資料庫中,所謂事務是指一組邏輯操作單元,使資料從一種狀態變換到另一種狀態。
我們把多個密不可分的操作看做是一個整體,那麼該整體就稱之為一個事務.
--------------------------------------------------
事務的ACID屬性:
1. 原子性(Atomicity)原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
2. 一致性(Consistency)事務必須使資料庫從一個一致性狀態變換到另外一個一致性狀態,但是不最終資料不能被破壞,兩個賬戶的總餘額是不能改變的.
3. 隔離性(Isolation):MySQL再講
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
4. 永續性(Durability)永續性是指一個事務一旦被提交,它對資料庫中資料的改變就是永久性的,接下來的其他操作和資料庫故障不應該對其有任何影響
--------------------------------------------------
事務:指構成單個邏輯工作單元的操作集合
事務處理:保證所有事務都作為一個工作單元來執行,即使出現了故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要麼所有的事務都被提交(commit),要麼整個事務回滾(rollback)到最初狀態
處理事務的兩個動作:
提交:commit: 當整個事務中,所有的邏輯單元都正常執行成功. ---->提交事務.---資料已經提交,不能更改.
回滾:rollback: 當整個事務中,有一個邏輯單元執行失敗, ---->回滾事務.
撤銷該事務中的所有操作,釋放鎖--->恢復到最初的狀態.
1):預設情況下,在JDBC中執行DML操作就會自動提交事務,此時我們得設定事務的手動提交機制(取消事務的自動提交).
2):查詢操作,不涉及資料的更改,所以不需要事務.
3):MySQL中InnoDB儲存引擎支援事務,MyISAM不支援.
alter table account engine = 'MyISAM';
意識:如果是DML操作時,沒有異常,程式碼也正確,但是資料改變不了,首先去想到事務沒有提交.
操作事務的模板:
try{
//取消事務自動提交:
connection物件.setAutoCommit(false);
操作1
操作2
操作3
//提交事務
Connection物件.commit();
}catch(Exception e){
//處理異常
//回滾事務
Connection物件.rollback();
}finally{
釋放資源
}
批處理操作
批量操作(batch):當需要成批插入或者更新記錄時。
可以採用Java的批量更新機制,這一機制允許多條語句一次性提交給資料庫批量處理。通常情況下比單獨提交處理更有效率.
JDBC的批量處理語句包括下面兩個方法:
addBatch(String sql):新增需要批量處理的SQL語句或是引數;
executeBatch();執行批量處理語句;
通常我們會遇到兩種批量執行SQL語句的情況:
多條SQL語句的批量處理; :Statement
一個SQL語句的批量傳參; :PreparedStatement
需求:同時向t_student表,插入5000條資料.
在JDBC中,MySQL不支援批量操作.
-------------------------------------------------------------------------
Statement 批處理 : 一次性可以執行多條sql語句,需要編譯多次。
應用場景:系統初始化 (建立表,建立資料等)
新增sql語句,st.addBatch(sql) --新增sql語句
批量處理sql語句,int[] st.executeBatch()
清除快取: st.clearBatch();
-------------------------------------------------------------------------
PreparedStatement 批處理 : 執行一條sql語句,編譯一次,執行sql語句的引數不同。
應用場景:表資料初始化
新增批量引數:psmt.addBatch() --新增實際引數,執行之前,需要執行psmt.setXxx()設定實際引數
執行批處理:int[] psmt.executeBatch()
清除快取:pstm.clearBatch();