作為規範,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中的一個簡單資料類
public class Musician {
private Long id;
private String name;
private Instrument mainInstrument;
private ArrayList performances = new ArrayList<Performance>();
public Musician( Long id, String name){ /* constructor setters... */ }
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setMainInstrument(Instrument instr){
this.instrument = instr;
}
public Instrument getMainInstrument(){
return this.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 = new Musician(0, "George Harrison");
musicianManager.save(georgeHarrison);複製程式碼
清單3用一行session.save()
替換了清單2中的手動SQL ,它指示JPA持久儲存該物件。從那時起,SQL轉換由框架處理,因此您永遠不必離開物件導向的範例。
JPA中的後設資料註釋
清單3中是配置的結果,該配置是使用JPA的註釋建立的。開發人員使用註釋來告知JPA應該保留哪些物件,以及如何保留它們。
清單4顯示了具有單個JPA註釋的Musician
類。
清單4. JPA的@Entity註釋
@Entity
public class Musician {
// ..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.指定主鍵
@Entity
public class Musician {
@Id
private Long id;複製程式碼
在這種情況下,我們使用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.註釋一對多關係
public class Musician {
@OneToMany
@JoinColumn(name="musicianId")
private List<Performance> performances = new ArrayList<Performance>();
//...
}複製程式碼
需要注意的一點是@JoinColumn
告訴JPA Performance表上的哪一列將對映到Musician
實體。每個performance都將與單個Musician
關聯,該列由此列跟蹤。當JPA將一個 Musician
或一個Performance
載入到資料庫中時,它將使用此資訊重新構建物件圖。
在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.persistence
eclipselink
2.5.0-RC1複製程式碼
您還需要包含資料庫的驅動程式,如清單9所示。
清單9. MySql聯結器的Maven依賴關係
mysql
mysql-connector-java
5.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-unit name="MyUnit" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/foo_bar"/>
<property name="javax.persistence.jdbc.user" value=""/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>
</persistence>複製程式碼
還有其他方法可以向系統提供此資訊,包括以程式設計方式。我建議使用該persistence.xml
檔案,因為以這種方式儲存依賴項使得在不修改程式碼的情況下更新應用程式非常容易。
JPA的Spring配置
使用Spring將極大地簡化JPA與應用程式的整合。例如,將@SpringBootApplication
註釋放在應用程式頭中會指示Spring 根據您指定的配置自動掃描類並根據需要注入EntityManager
。
如果您希望Spring為您的應用程式提供JPA支援,清單11顯示了要包含的依賴項。
清單11.在Maven中新增Spring JPA支援
org.springframework.boot
spring-boot-starter
2.1.3.RELEASE
org.springframework.boot
spring-boot-starter-data-jpa
2.1.3.RELEASE複製程式碼
結論
處理資料庫的每個應用程式都應該定義一個應用程式層,其唯一目的是隔離永續性程式碼。正如您在本文中看到的,Java Persistence API引入了一系列功能並支援Java物件永續性。簡單的應用程式可能不需要JPA的所有功能,在某些情況下,配置框架的開銷可能不值得。然而,隨著應用程式的增長,JPA的結構和封裝確實能夠保持不變。使用JPA可以簡化目的碼,並提供用於訪問Java應用程式中的資料的傳統框架。
英文原文:www.javaworld.com/article/337…
公眾號:銀河系1號
聯絡郵箱:public@space-explore.com
(未經同意,請勿轉載)