做爲規範,Java Persistence API關注持久性,它將Java對象的建立過程和具體的建立形式解耦。並不是全部Java對象都須要持久化,但大多數應用程序都會保留關鍵業務對象。JPA規範容許您定義應該保留哪些對象,以及如何在Java應用程序中保留這些對象。html
JPA自己不是一個工具或框架; 相反,它定義了一組能夠由任何工具或框架實現的概念。雖然JPA的對象關係映射(ORM)模型最初基於Hibernate,但它已經發展了。一樣,雖然JPA最初打算用於關係/ SQL數據庫,可是一些JPA實現已經擴展用於NoSQL數據存儲。支持JPA和NoSQL的流行框架是EclipseLink,它是JPA 2.2的參考實現。前端
因爲它們交織在一塊兒的歷史,Hibernate和JPA常常混爲一談。可是,與Java Servlet規範同樣,JPA產生了許多兼容的工具和框架; Hibernate只是其中之一。java
Hibernate由Gavin King開發,於2002年初發布,是一個用於Java的ORM庫。King開發了Hibernate做爲持久化實體bean的替代品。該框架很是受歡迎,當時很是須要,它的許多想法都在第一個JPA規範中被採用和編纂。mysql
今天,Hibernate ORM是最成熟的JPA實現之一,而且仍然是Java中ORM的流行選項。Hibernate ORM 5.3.8(撰寫本文時的當前版本)實現了JPA 2.2。此外,Hibernate的工具系列已經擴展到包括Hibernate Search,Hibernate Validator和Hibernate OGM等流行工具,後者支持NoSQL的域模型持久性。web
雖然它們的執行不一樣,但每一個JPA實現都提供某種ORM層。爲了理解JPA和JPA兼容的工具,您須要掌握ORM。對象關係映射是一項任務- 開發人員有充分的理由避免手動執行。像Hibernate ORM或EclipseLink這樣的框架將該任務編碼爲庫或框架,即ORM層。做爲應用程序體系結構的一部分,ORM層負責管理軟件對象的轉換,以便與關係數據庫中的表和列進行交互。在Java中,ORM層轉換Java類和對象,以即可以在關係數據庫中存儲和管理它們。spring
默認狀況下,持久化對象的名稱將成爲表的名稱,字段將成爲列。設置表後,每一個錶行對應於應用程序中的對象。對象映射是可配置的,但默認值每每效果很好。sql
圖1說明了JPA和ORM層在應用程序開發中的做用。數據庫
設置新項目以使用JPA時,須要配置數據存儲區和JPA提供程序。您將配置數據存儲鏈接器以鏈接到您選擇的數據庫(SQL或NoSQL)。您還將包含和配置JPA提供程序,它是一個框架,如Hibernate或EclipseLink。雖然您能夠手動配置JPA,但許多開發人員選擇使用Spring的開箱即用支持。有關手動和基於Spring的JPA安裝和設置的演示,請參閱下面的「 JPA安裝和設置 」。apache
Java Data Objects是一個標準化的持久性框架,它與JPA的不一樣之處主要在於支持對象中的持久性邏輯,以及它長期以來對使用非關係數據存儲的支持。JPA和JDO足夠類似,JDO提供者也常常支持JPA。請參閱Apache JDO項目,以瞭解有關JDO與JPA和JDBC等其餘持久性標準相關的更多信息。編程
從編程的角度來看,ORM層是一個適配器層:它使對象圖的語言適應SQL和關係表的語言。ORM層容許面向對象的開發人員構建持久保存數據的軟件,而無需離開面向對象的範例。
使用JPA時,能夠建立從數據存儲區到應用程序的數據模型對象的映射。您能夠定義對象和數據庫之間的映射,而不是定義對象的保存和檢索方式,而後調用JPA來保存它們。若是您正在使用關係數據庫,那麼應用程序代碼和數據庫之間的大部分實際鏈接將由JDBC(Java數據庫鏈接API)處理。做爲規範,JPA提供元數據註釋,您可使用它來定義對象和數據庫之間的映射。每一個JPA實現都爲JPA註釋提供了本身的引擎。JPA規範還提供了PersistanceManager
或者EntityManager
,它們是與JPA系統聯繫的關鍵點(其中您的業務邏輯代碼告訴系統如何處理映射對象)。
爲了使全部這些更具體,請考慮清單1,這是一個用於爲音樂家建模的簡單數據類。
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是軟件開發的常見功能。雖然它們包含多種數據,但它們不包含任何業務邏輯。持久化數據對象是軟件開發中廣泛存在的挑戰。
將Musician
類的實例保存到關係數據庫的一種方法是使用JDBC庫。JDBC是一個抽象層,它容許應用程序發出SQL命令而無需考慮底層數據庫實現。
清單2顯示瞭如何使用JDBC 來持久化Musician
類。
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自己就成了一項任務。
如今考慮清單3,咱們使用JPA 持久化Musician
類。
Musician georgeHarrison = new Musician(0, "George Harrison");
musicianManager.save(georgeHarrison);複製代碼
清單3用一行session.save()
替換了清單2中的手動SQL ,它指示JPA持久保存該對象。從那時起,SQL轉換由框架處理,所以您永遠沒必要離開面向對象的範例。
清單3中是配置的結果,該配置是使用JPA的註釋建立的。開發人員使用註釋來告知JPA應該保留哪些對象,以及如何保留它們。
清單4顯示了具備單個JPA註釋的Musician
類。
@Entity
public class Musician {
// ..class body
}複製代碼
持久對象有時稱爲實體。附加@Entity
到類,Musician
告知JPA應該保留此類及其對象。
與大多數現代框架同樣,JPA遵循約定編碼(也稱爲約定優於配置),其中框架提供基於行業最佳實踐的默認配置。做爲一個示例,名爲Musician
的類將默認映射到名爲Musician的數據庫表。
傳統配置是節省時間的,而且在許多狀況下它運行良好。也能夠自定義JPA配置。例如,您可使用JPA的@Table
註釋來指定應該存儲Musician
類的表。
@Entity
@Table(name="musician")
public class Musician {
// ..class body
}複製代碼
清單5告訴JPA將實體(Musician
類)持久化到musician
表中。
在JPA中,主鍵是用於惟一標識數據庫中每一個對象的字段。主鍵可用於引用對象並將對象與其餘實體相關聯。每當您在表中存儲對象時,您還將指定要用做其主鍵的字段。
在清單6中,咱們告訴JPA要使用哪一個字段做爲Musician
主鍵。
@Entity
public class Musician {
@Id
private Long id;複製代碼
在這種狀況下,咱們使用JPA的@Id
註釋將id
字段指定爲Musician
主鍵。默認狀況下,此配置假定主鍵將由數據庫設置 - 例如,當字段設置爲在表上自動遞增時。
JPA支持生成對象主鍵的其餘策略。它還有用於更改單個字段名稱的註釋。一般,JPA足夠靈活,能夠適應您可能須要的任何持久性映射。
將類映射到數據庫表並創建其主鍵後,便可擁有在數據庫中建立,檢索,刪除和更新該類所需的一切。調用session.save()
將建立或更新指定的類,具體取決於主鍵字段是否爲null或是否適用於現有實體。調用entityManager.remove()
將刪除指定的類。
簡單地使用原始字段持久化對象只是方程式的一半。JPA還具備管理彼此相關實體的能力。在表和對象中都有四種實體關係:
每種類型的關係描述了實體與其餘實體的關係。例如,Musician
實體能夠與由諸如List
或Set
的集合表示的實體具備一對多的關係。
若是Musician
包含一個Band
字段,這些實體之間的關係能夠是多對一的,這意味着在單個Band
類上有Musician集合
。(假設每一個音樂家只在一個樂隊中演奏。)
若是Musician
包含BandMates
字段,則能夠表示與其餘Musician
實體的多對多關係。
最後,Musician
可能與Quote
實體有一對一的關係,用於表示一個着名的引語:Quote famousQuote = new Quote()
。
JPA爲每種關係映射類型提供註解。清單7顯示瞭如何註解Musician
和Performance
s 之間的一對多關係。
public class Musician {
@OneToMany
@JoinColumn(name="musicianId")
private List<Performance> performances = new ArrayList<Performance>();
//...
}複製代碼
須要注意的一點是@JoinColumn
告訴JPA Performance表上的哪一列將映射到Musician
實體。每一個performance都將與單個Musician
關聯,該列由此列跟蹤。當JPA將一個 Musician
或一個Performance
加載到數據庫中時,它將使用此信息從新構建對象圖。
除了知道在數據庫中放置相關實體的位置以外,JPA還須要知道如何加載它們。
Musician
類有一個
bandMate
字段(如清單7所示),加載
george
可能致使整個
Musician
表從數據庫加載!
咱們須要的是定義相關實體的延遲加載的能力- 固然,認識到JPA中的關係多是eager或lazy的。您可使用註釋來自定義提取策略,但JPA的默認配置一般能夠直接使用,無需更改:
最後,咱們將簡要介紹如何爲Java應用程序安裝和設置JPA。在本演示中,我將使用EclipseLink,即JPA參考實現。安裝JPA的經常使用方法是在項目中包含JPA提供程序。清單8顯示瞭如何將EclipseLink做爲Maven pom.xml
文件中的依賴項包含在內。
org.eclipse.persistence
eclipselink
2.5.0-RC1複製代碼
您還須要包含數據庫的驅動程序,如清單9所示。
mysql
mysql-connector-java
5.1.32複製代碼
接下來,您須要告訴系統您的數據庫和提供程序。這在persistence.xml
文件中完成,如清單10所示。
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
文件,由於以這種方式存儲依賴項使得在不修改代碼的狀況下更新應用程序很是容易。
使用Spring將極大地簡化JPA與應用程序的集成。例如,將@SpringBootApplication
註釋放在應用程序頭中會指示Spring 根據您指定的配置自動掃描類並根據須要注入EntityManager
。
若是您但願Spring爲您的應用程序提供JPA支持,清單11顯示了要包含的依賴項。
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
(未經贊成,請勿轉載)