2020年5月23日 星期六

JDBC、JPA、JPQL、CriteriaAPI簡介

JDBC與ORM

我們小時候都有騎自行車的經驗,自行車也幾乎是每個人啟蒙的交通工具。隨著年紀逐漸成長,上學、上班可能需要長程通勤,因此會選擇機車、汽車、鐵路、甚至高鐵等,視需求而定。但不論如何,短程距離還是可能會使用自行車,如共享單車等,因此騎自行車的基本技能還是需要的!
JDBC等同於我們談論「資料保存(persistence)」主題時的自行車。它雖然是基礎,但功能性完全沒問題,存取資料庫不會有JDBC做不到的功能,畢竟JDBC就是Java為存取資料庫而定義的介面。但是隨著需求變得越來越複雜,我們對資料保存的需求也隨之提高,比如說我們需要將Java物件的屬性欄位對應到資料庫的表格欄位,並且為我們建立查詢語句,使我們不需要如使用PreparedStatement時輸入一連串的問號。除此之外我們還需要更複雜的功能:

  1. lazy loading
  2. 隨著物件間的關聯愈來愈多,物件圖譜(object graphs)變得愈加龐大,若關聯物件的屬性欄位的資料都來自資料庫,有時不會希望在由資料庫載入物件時立即載入(eager loading)所有相關資料。比如說系統需要載入PurchaseOrder的集合物件,而每一個物件又包含了LineItem的集合物件;如果只需要PurchaseOrder的其他屬性,事實上一併載入LineItem資料是沒有任何意義的,而且將增加資料庫與JVM的負荷。lazy loading允許僅在需要時才獲取資料。物件圖譜(object graphs)的名詞說明請參閱「Java SE8 OCPJP進階認證指南」一書的「8.4.1了解序列化技術」。
  3. eager fetching
  4. 與lazy loading或eager loading不同,藉由這個機制可以在一個查詢中獲取整個object graphs而不需要以其他SQL反覆來回資料庫與JVM間去加載資料至Java物件中。以前例來說,就是以1個查詢取得PurchaseOrder物件與其關聯的LineItem。
  5. cascading
  6. 有時候對資料庫表格的更改會希望觸發其他連動行為,如刪除PurchaseOrder後,會希望一併刪除關聯的LineItem;或是新增PurchaseOrder時,關聯LineItem一併新增,不需要再特地呼叫EntityManager的persist()或remove()方法。
提供這些進階服務的框架,通稱為object-relational mapping,或簡稱ORM。使用ORM框架開發persistence相關程式可以為開發者減少大量程式碼與時間,也可以將重心從編寫容易出錯的SQL程式碼轉移到商業邏輯需求。
ORM在Java裡已經是正式規格,稱「JPA」。

JPA

Java Persistence API (JPA)扮演Java程式語言和各式資料庫間的橋樑,用於將Java物件對應到關聯式資料庫表格的資料列,因此如同JDBC只定義規格與抽象層,實作則由各供應商負責。JPA的規格讓程式開發人員即便面對不同種類資料庫的資料也能以一致的做法進行新增、讀取、修改、刪除(create/read/update/delete,CRUD)操作,以便相同的程式碼適用於不同的資料庫產品。
JPA不僅可以取代傳統JDBC程式碼處理與資料庫的互動,還可以在適當定義後將資料列的欄位(fields)自動對應(mapping)到domain物件的欄位(fields)以增加便利性,入門觀念可參閱「Java RWD Web企業網站開發指南」一書的「15.3 Object Relational Mapping(ORM)的軟體架構與JPA」。
JPA的組成主要有3個部分:

  1. 實體(Entity)類別
  2. 較舊的JPA版本的實體(Entity)類別需繼承JPA框架提供的類別,導致對框架的依賴性且不容易實施單元測試;較新的JPA版本則使用單純的POJO物件(plain old Java object)作為Entity類別,因此不需要繼承任何框架提供的類別。這類概念風潮由Spring框架帶起,可參閱「Java RWD Web企業網站開發指南」一書的「18.1 Spring 的目的與策略」。Entity類別在執行時期產生的Entity物件則藉由Entity manager和persistent context管理。
  3. ORM (object-relational mapping)架構
  4. Entity類別藉由XML設定檔或annotation標註類別來定義類別欄位與RDBMS資料表欄位的對應關係(ORM, object-relational mapping)的詮釋資料(metadata)。
  5. JPQL (Java Persistence Query Language)查詢語言
  6. JPA除了對不同的資料庫產品建立一致的抽象層,也提供了JPQL以取代各家資料庫紛雜的查詢語言(native query language)。JPQL讓程式開發人員可以對不同種類的資料庫使用一致的查詢語言,並在執行時期依資料庫種類由框架轉換為一般SQL進行資料庫存取。

JPQL

JDBC查詢資料庫使用SQL語言,可以是符合ANSI 92的標準SQL、或是各資料支援的特殊SQL。
藉由Java Persistence Query Language (JPQL)則允許我們以「類似」SQL語法的Java字串存取資料庫。JPQL是建構於各類SQL之上的抽象層,因此可以使用一致的作法存取各種資料庫,具有獨立於各類資料庫的可攜性(portable)特性。

Criteria API

JPA使用JPQL查詢和JDBC使用原生SQL語言查詢相似,都是使用字串來定義;JPA的Criteria API則藉由分析並分拆SQL指令為數個結構,分別以Java物件取代,因此開發上有較大不同。不過,無論是:
  1. 以字串為基礎的JPQL。
  2. 以物件為基礎的Criteria API。
兩者共通性為:
  1. 效能(performance)相似。
  2. 本質上都具備可攜性(portable),不會因為更換底層資料庫而必須修改程式碼。
由於JPQL如同原生SQL以字串方式來定義,因此語法或邏輯的錯誤必須在執行(runtime)時期才能發現;Criteria API的優點是使用Java物件組成,因此若有語法或邏輯上的錯誤可以在編譯(compile)時期提前發現,如同enum都屬於「型態安全(typesafe)」的設計。
另一方面,SQL語言已經脫離Java語言的範疇,不是每個Java程式設計師都能駕輕就熟;若不熟悉SQL語言,改以物件的方式呈現SQL組成,對於他們可能更容易理解並使用。
此外,對於會因為情境不同而需要「動態」組織的SQL指令,使用Criteria API也是一個讓程式更好維護的選項,因為可以減少字串連接的操作以避免bug。
為了讓Criteria API的型態安全的效益可以更發揮,經常會搭配使用「Metamodel API」,這也是特色之一。

沒有留言:

張貼留言