iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
自我挑戰組

Java SE系列 第 28

Day28:阿賴耶識

程式寫了老半天,說到底就是為了處理資料。

不管處理邏輯使用了多少技術,到頭來終究會得出一個結果,並且需要將這個結果儲存起來,這就是資料庫的用處了。

  • JDBC概念

在Java中我們會使用JDBC來進行資料庫的操作,有統一的語法來進行,不過這些都是Java官方高級工程師們的心血結晶,在使用的同時了解一下背後原理也會用得安心一些,發生問題也大概知道要修哪裡。

下圖就是JDBC的概念圖:
JDBC
我們在程式碼寫的會是JDBC code,而JDBC其實就是一組介面,真正提供介面實作內容的是各家資料庫廠商的驅動程式(driver),為什麼要這麼做?

資料庫不是只有一種,有好多廠商都推出自家的資料庫,賺錢嘛,提供給商用公司就可以收使用權費用。
但這就衍生出一個問題,每家資料庫都有自己的通訊協定,這樣開發好的程式如果以後要用另一家廠商的資料庫,那不就要重寫?

所以Java官方提出了JDBC的解決方案,Java開發者只要專心寫JDBC code,要用哪家資料庫就只要再載入該家資料庫的driver,替介面注入實作內容,一切就都可以運作了~ 這也完全符合write once, run everywhere的宗旨。

  • SQL Injection

在我們寫JDBC的時候會有Statement以及PreparedStatement兩種放入SQL語句的物件,我們都會說用Statement需要直接放入完整的SQL語句,但程式中常常需要用字串連接的方式加上變數,這就導致有SQL Injection的可能了。下面舉個例子:

Scanner scanner = new Scanner(System.in);
System.out.println("Please enter username:");
String username = scanner.next();
System.out.println("Please enter password:");
String password = scanner.next();
String sql = "SELECT * FROM USER WHERE username = '" + username + "' and password = '" + password + "'";
Connection conn = DriverManager.getConnection("jdbc:xxxx....", "someUserName", "somePwd");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

範例中會藉由console輸入的username和password來尋找資料庫中USER表格相對應的user資料。如果這時候我們在console要輸入username的時候輸入以下:

Please enter username:
Jack' OR 1=1; --
Please enter password:
whatever

密碼隨便輸入沒差,我們來看看這樣sql會變什麼:

SELECT * FROM USER WHERE username = 'Jack' OR 1=1; --' and password = 'whatever'

"--"在某些資料庫中就是註解的符號,所以上面的sql語句在";"後面都被註解掉了,而我們WHERE條件式不管username=什麼,加上了"OR 1=1"就一定會是true,所以這個sql就會找出USER表格中所有的user!

這樣的情況就是被SQL Injection了。

  • 交易(Transaction)

    1. ACID
      交易要符合ACID,Atomicity(原子性)、Consistency()、Isolation(隔離性)、Durability(持續性)。
      • Atomicity:一個交易就是一個最小單位,不管其中有多少步驟,都要全部達成或全部撤銷。
      • Consistency:資料庫中的狀態在進行過交易後要有一致性,有點像是原子性用另一個角度來詮釋,原子性 著重在交易本身,而一致性看的是被交易操作的資料庫。
      • Isolation:不同交易之間不可互相影響,或者互相影響的程度需符合資料庫所設定的隔離等級。
      • Durability:交易完成之後,資料庫完成交易的狀態不會因為任何因素有所改變。
    2. Isolation Level (隔離等級)
      交易與交易之間一定會有時間差的問題,這時候就需要設定隔離等級來進行管理。
      • Read Uncommitted
        目的在解決Lost Update的現象,假如有2個交易都要對同一個欄位進行更改,A交易更改後commit,B交易在A交易commit之前也進行了更改,但是是在A交易commit之後才commit,這時候A交易commit的內容就會被B交易覆蓋掉了,稱為Lost Update。若資料庫隔離等級為Read Uncommitted,在A交易開始後,B交易就只能讀取(會讀到A交易尚未commit的更改),無法進行更改操作。
      • Read Committed
        假如A交易尚未commit前對某欄位做了更改,這時候B交易讀到了這個更改,但是A交易後來rollback,那B交易讀取到的資料就是錯誤的了,這個現象稱為Dirty Read。若資料庫隔離等級為Read Committed,那B交易在讀取的時候,就不會讀到A交易尚未commit的變更了,而會是commit過的狀態。
      • Repeatable Read
        如果A交易現在沒有要進行變更,而是進行讀取,在開始交易後讀取了某欄位的值,這時交易B開始,對該欄位進行了變更並commit,此時尚未commit的交易A再次進行讀取,就會讀取到B交易commit過後的結果了,這種現象稱為Unrepeatable Read。要解決這個現象可以把資料庫隔離等級設定為Repeatable Read。
      • Serializable
        如果A交易開始進行並讀取某表格的資料筆數假設是5筆,這時B交易開始並新增了一筆資料後commit,此時A交易再度進行讀取就會多了一筆資料,同一交易中讀取到的資料筆數不一致,這種現象稱為Phantom Read。要解決可以把資料庫隔離等級設定為Serializable,但是這樣的話資料庫效率變差,因為所有事情為了保證不發生Phantom Read就只能讓交易循序一個一個來。

上一篇
Day27:危機意識
下一篇
Day29:翻譯蒟蒻
系列文
Java SE30

尚未有邦友留言

立即登入留言