1. 程式人生 > >從零開始學後端(4)——JDBC的重構設計

從零開始學後端(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();