作為規范,?Java Persistence API?關注?持久性?,它將Java對象的創建過程和具體的創建形式解耦。并非所有Java對象都需要持久化,但大多數應用程序都會保留關鍵業務對象。JPA規范允許您定義應該保留?哪些?對象,以及?如何?在Java應用程序中保留這些對象。
JPA本身不是一個工具或框架; 相反,它定義了一組可以由任何工具或框架實現的概念。雖然JPA的對象關系映射(ORM)模型最初基于?Hibernate?,但它已經發展了。同樣,雖然JPA最初打算用于關系/ SQL數據庫,但是一些JPA實現已經擴展用于NoSQL數據存儲。支持JPA和NoSQL的流行框架是?EclipseLink?,它是JPA 2.2的參考實現。
JPA和Hibernate
由于它們交織在一起的歷史,Hibernate和JPA經常混為一談。但是,與?Java Servlet?規范一樣,JPA產生了許多兼容的工具和框架; Hibernate只是其中之一。
Hibernate?由Gavin King開發,于2002年初發布,是一個用于Java的ORM庫。King開發了Hibernate作為?持久化實體bean?的?替代品?。該框架非常受歡迎,當時非常需要,它的許多想法都在第一個JPA規范中被采用和編纂。
今天,?Hibernate ORM?是最成熟的JPA實現之一,并且仍然是Java中ORM的流行選項。?Hibernate ORM 5.3.8?(撰寫本文時的當前版本)實現了JPA 2.2。此外,Hibernate的工具系列已經擴展到包括?Hibernate Search?,?Hibernate Validator?和?Hibernate OGM?等流行工具,后者支持NoSQL的域模型持久性。
什么是Java ORM?
雖然它們的執行不同,但每個JPA實現都提供某種ORM層。為了理解JPA和JPA兼容的工具,您需要掌握ORM。
對象關系映射是一項?任務?- 開發人員有充分的理由避免手動執行。像Hibernate ORM或EclipseLink這樣的框架將該任務編碼為庫或框架,即?ORM層?。作為應用程序體系結構的一部分,ORM層負責管理軟件對象的轉換,以便與關系數據庫中的表和列進行交互。在Java中,ORM層轉換Java類和對象,以便可以在關系數據庫中存儲和管理它們。
默認情況下,持久化對象的名稱將成為表的名稱,字段將成為列。設置表后,每個表行對應于應用程序中的對象。對象映射是可配置的,但默認值往往效果很好。
圖1說明了JPA和ORM層在應用程序開發中的作用。
配置Java ORM層
設置新項目以使用JPA時,需要配置數據存儲區和JPA提供程序。您將配置?數據存儲連接器?以連接到您選擇的數據庫(SQL或NoSQL)。您還將包含和配置?JPA提供程序?,它是一個框架,如Hibernate或EclipseLink。雖然您可以手動配置JPA,但許多開發人員選擇使用Spring的開箱即用支持。有關手動和基于Spring的JPA安裝和設置的演示,請參閱下面的“?JPA安裝和設置?”。
Java數據對象
Java Data Objects是一個標準化的持久性框架,它與JPA的不同之處主要在于支持對象中的持久性邏輯,以及它長期以來對使用非關系數據存儲的支持。JPA和JDO足夠相似,JDO提供者也經常支持JPA。請參閱?Apache JDO項目?,以了解有關JDO與JPA和JDBC等其他持久性標準相關的更多信息。
Java中的數據持久性
從編程的角度來看,ORM層是一個?適配器層?:它使對象圖的語言適應SQL和關系表的語言。ORM層允許面向對象的開發人員構建持久保存數據的軟件,而無需離開面向對象的范例。
使用JPA時,可以創建從數據存儲區到應用程序的數據模型對象的?映射?。您可以定義對象和數據庫之間的映射,而不是定義對象的保存和檢索方式,然后調用JPA來保存它們。如果您正在使用關系數據庫,那么應用程序代碼和數據庫之間的大部分實際連接將由JDBC(?Java數據庫連接API)處理?。
作為規范,JPA提供?元數據注釋?,您可以使用它來定義對象和數據庫之間的映射。每個JPA實現都為JPA注釋提供了自己的引擎。JPA規范還提供了 PersistanceManager 或者 EntityManager ,它們是與JPA系統聯系的關鍵點(其中您的業務邏輯代碼告訴系統如何處理映射對象)。
為了使所有這些更具體,請考慮清單1,這是一個用于為音樂家建模的簡單數據類。
清單1. Java中的一個簡單數據類
publicclassMusician{privateLong id;privateString name;privateInstrument mainInstrument;privateArrayList performances =newArrayList();publicMusician( Long id, String name){/* constructor setters... */}publicvoidsetName(String name){this.name = name; }publicStringgetName(){returnthis.name; }publicvoidsetMainInstrument(Instrument instr){this.instrument = instr; }publicInstrumentgetMainInstrument(){returnthis.instrument; }// ...Other getters and setters...}
清單1中的 Musician 類用于保存數據。它可以包含原始數據,例如?名稱?字段。它還可以與其他類(如 mainInstrument 和 performances )保持關系。
Musician 存在的?原因?是包含數據。這種類有時稱為DTO或?數據傳輸對象?。DTO是軟件開發的常見功能。雖然它們包含多種數據,但它們不包含任何業務邏輯。持久化數據對象是軟件開發中普遍存在的挑戰。
JDBC的數據持久性
將 Musician 類的實例保存到關系數據庫的一種方法是使用JDBC庫。JDBC是一個抽象層,它允許應用程序發出SQL命令而無需考慮底層數據庫實現。
清單2顯示了如何使用JDBC 來持久化 Musician 類。
清單2.插入記錄的JDBC
Musician georgeHarrison = new Musician(0,"George Harrison"); String myDriver ="org.gjt.mm.mysql.Driver"; String myUrl ="jdbc:mysql://localhost/test"; Class.forName(myDriver); Connection conn = DriverManager.getConnection(myUrl,"root",""); String query =" insert into users (id, name) values (?, ?)"; PreparedStatement preparedStmt = conn.prepareStatement(query); preparedStmt.setInt (1,0); preparedStmt.setString (2,"George Harrison"); preparedStmt.setString (2,"Rubble"); preparedStmt.execute(); conn.close();// Error handling removed for brevity
清單2中的代碼是相當自我記錄的。該 georgeHarrison 對象可以來自任何地方(前端提交,外部服務等),并設置其ID和name字段。然后,對象上的字段用于提供SQL insert 語句的值。( PreparedStatement 該類是JDBC的一部分,提供了一種將值安全地應用于SQL查詢的方法。)
雖然JDBC允許手動配置附帶的控件,但與JPA相比,它很麻煩。要修改數據庫,首先需要創建一個SQL查詢,該查詢從Java對象映射到關系數據庫中的表。然后,只要對象簽名發生更改,就必須修改SQL。使用JDBC,維護SQL本身就成了一項任務。
JPA的數據持久性
現在考慮清單3,我們使用JPA 持久化 Musician 類。
清單3.使用JPA保留George Harrison
Musician georgeHarrison =newMusician(0,"George Harrison");musicianManager.save(georgeHarrison);
清單3用一行 session.save() 替換了清單2中的手動SQL ,它指示JPA持久保存該對象。從那時起,SQL轉換由框架處理,因此您永遠不必離開面向對象的范例。
JPA中的元數據注釋
清單3中的魔力是?配置?的結果,該?配置?是使用JPA的?注釋?創建的。開發人員使用注釋來告知JPA應該保留哪些對象,以及如何保留它們。
清單4顯示了具有單個JPA注釋的 Musician 類。
清單4. JPA的@Entity注釋
@EntitypublicclassMusician{// ..class body}
持久對象有時稱為?實體?。附加 @Entity 到類, Musician 告知JPA應該保留此類及其對象。
配置JPA
與大多數現代框架一樣,JPA?遵循約定編碼?(也稱為約定優于配置),其中框架提供基于行業最佳實踐的默認配置。作為一個示例,名為 Musician 的類將默認映射到名為?Musician?的數據庫表。
傳統配置是節省時間的,并且在許多情況下它運行良好。也可以自定義JPA配置。例如,您可以使用JPA的 @Table 注釋來指定應該存儲 Musician 類的表。
清單5. JPA的@Table注釋
@Entity@Table(name="musician")public class Musician {// ..class body}
清單5告訴JPA將實體( Musician 類)持久化到 musician 表中。
主鍵
在JPA中,?主鍵?是用于唯一標識數據庫中每個對象的字段。主鍵可用于引用對象并將對象與其他實體相關聯。每當您在表中存儲對象時,您還將指定要用作其主鍵的字段。
在清單6中,我們告訴JPA要使用哪個字段作為 Musician 主鍵。
清單6.指定主鍵
@EntitypublicclassMusician{@IdprivateLongid;
在這種情況下,我們使用JPA的 @Id 注釋將 id 字段指定為 Musician 主鍵。默認情況下,此配置假定主鍵將由數據庫設置 - 例如,當字段設置為在表上自動遞增時。
JPA支持生成對象主鍵的其他策略。它還有用于更改單個字段名稱的注釋。通常,JPA足夠靈活,可以適應您可能需要的任何持久性映射。
CRUD操作
將類映射到數據庫表并建立其主鍵后,即可擁有在數據庫中創建,檢索,刪除和更新該類所需的一切。調用 session.save() 將創建或更新指定的類,具體取決于主鍵字段是否為null或是否適用于現有實體。調用 entityManager.remove() 將刪除指定的類。
JPA中的實體關系
簡單地使用原始字段持久化對象只是方程式的一半。JPA還具有管理彼此相關實體的能力。在表和對象中都有四種實體關系:
一到多
許多到一
許多一對多
一比一
每種類型的關系描述了實體與其他實體的關系。例如, Musician 實體可以與由諸如 List 或 Set 的集合表示的實體具有?一對多的關系?。
如果 Musician 包含一個 Band 字段,這些實體之間的關系可以是?多對一的?,這意味著在單個 Band 類上有 Musician集合 。(假設每個音樂家只在一個樂隊中演奏。)
如果 Musician 包含 BandMates 字段,則可以表示與其他 Musician 實體?的多對多關系。
最后, Musician 可能與 Quote 實體有?一對一的關系?,用于表示一個著名的引語: Quote famousQuote = new Quote() 。
定義關系類型
JPA為每種關系映射類型提供注解。清單7顯示了如何注解 Musician 和 Performance s 之間的一對多關系。
清單7.注釋一對多關系
publicclassMusician{@OneToMany@JoinColumn(name="musicianId") private List performances = new ArrayList();//...}
需要注意的一點是 @JoinColumn 告訴JPA?Performance?表上的哪一列將映射到 Musician 實體。每個performance都將與單個 Musician 關聯,該列由此列跟蹤。當JPA將一個 Musician 或一個 Performance 加載到數據庫中時,它將使用此信息重新構建對象圖。
在JPA中獲取策略
除了知道?在?數據庫中放置相關實體??的位置之外,JPA還需要知道?如何?加載它們。?獲取策略?告訴JPA如何加載相關實體。加載和保存對象時,JPA框架必須能夠微調對象圖的處理方式。例如,如果 Musician 類有一個 bandMate 字段(如清單7所示),加載 george 可能導致整個?Musician?表從數據庫加載!
我們需要的是定義相關實體的?延遲加載?的能力- 當然,認識到?JPA?中的?關系可能是eager或?lazy的。您可以使用注釋來自定義提取策略,但JPA的默認配置通常可以直接使用,無需更改:
一對多:lazy
多對一:eager
多對多:lazy
一對一:eager
JPA安裝和設置
最后,我們將簡要介紹如何為Java應用程序安裝和設置JPA。在本演示中,我將使用EclipseLink,即JPA參考實現。
安裝JPA的常用方法是在項目中?包含?JPA提供程序。清單8顯示了如何將EclipseLink作為Maven pom.xml 文件中的依賴項包含在內。
清單8.將EclipseLink包含為Maven依賴項
org.eclipse.persistenceeclipselink2.5.0-RC1
您還需要包含數據庫的驅動程序,如清單9所示。
清單9. MySql連接器的Maven依賴關系
mysqlmysql-connector-java5.1.32
接下來,您需要告訴系統您的數據庫和提供程序。這在 persistence.xml 文件中完成,如清單10所示。
清單10. Persistence.xml
http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
還有其他方法可以向系統提供此信息,包括以編程方式。我建議使用該 persistence.xml 文件,因為以這種方式存儲依賴項使得在不修改代碼的情況下更新應用程序非常容易。
清單10. Persistence.xml
http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
還有其他方法可以向系統提供此信息,包括以編程方式。我建議使用該 persistence.xml 文件,因為以這種方式存儲依賴項使得在不修改代碼的情況下更新應用程序非常容易。
JPA的Spring配置
使用Spring將極大地簡化JPA與應用程序的集成。例如,將 @SpringBootApplication 注釋放在應用程序頭中會指示Spring 根據您指定的配置自動掃描類并根據需要注入 EntityManager 。
如果您希望Spring為您的應用程序提供JPA支持,清單11顯示了要包含的依賴項。
清單11.在Maven中添加Spring JPA支持
org.springframework.bootspring-boot-starter2.1.3.RELEASEorg.springframework.bootspring-boot-starter-data-jpa2.1.3.RELEASE
結論
處理數據庫的每個應用程序都應該定義一個應用程序層,其唯一目的是隔離持久性代碼。正如您在本文中看到的,Java Persistence API引入了一系列功能并支持Java對象持久性。簡單的應用程序可能不需要JPA的所有功能,在某些情況下,配置框架的開銷可能不值得。然而,隨著應用程序的增長,JPA的結構和封裝確實能夠保持不變。使用JPA可以簡化目標代碼,并提供用于訪問Java應用程序中的數據的傳統框架。
喜歡的點點關注,點點贊。
對Java技術,架構技術感興趣的同學,歡迎加QQ群585550789,一起學習,相互討論。
群內已經有小伙伴將知識體系整理好(源碼,筆記,PPT,學習視頻),歡迎加群領取。