作為規(guī)范,Java Persistence API關(guān)注持久性妥粟,它將Java對象的創(chuàng)建過程和具體的創(chuàng)建形式解耦勾给。并非所有Java對象都需要持久化播急,但大多數(shù)應(yīng)用程序都會保留關(guān)鍵業(yè)務(wù)對象桩警。JPA規(guī)范允許您定義應(yīng)該保留哪些對象握截,以及如何在Java應(yīng)用程序中保留這些對象川蒙。
JPA本身不是一個工具或框架; 相反,它定義了一組可以由任何工具或框架實現(xiàn)的概念昼牛。雖然JPA的對象關(guān)系映射(ORM)模型最初基于Hibernate术瓮,但它已經(jīng)發(fā)展了。同樣辜伟,雖然JPA最初打算用于關(guān)系/ SQL數(shù)據(jù)庫,但是一些JPA實現(xiàn)已經(jīng)擴展用于NoSQL數(shù)據(jù)存儲脊另。支持JPA和NoSQL的流行框架是EclipseLink导狡,它是JPA 2.2的參考實現(xiàn)。
JPA和Hibernate
由于它們交織在一起的歷史偎痛,Hibernate和JPA經(jīng)澈蹬酰混為一談。但是踩麦,與Java Servlet規(guī)范一樣枚赡,JPA產(chǎn)生了許多兼容的工具和框架; Hibernate只是其中之一。
Hibernate由Gavin King開發(fā)贫橙,于2002年初發(fā)布,是一個用于Java的ORM庫。King開發(fā)了Hibernate作為持久化實體bean的替代品。該框架非常受歡迎,當(dāng)時非常需要,它的許多想法都在第一個JPA規(guī)范中被采用和編纂。
今天,Hibernate ORM是最成熟的JPA實現(xiàn)之一,并且仍然是Java中ORM的流行選項。Hibernate ORM 5.3.8(撰寫本文時的當(dāng)前版本)實現(xiàn)了JPA 2.2。此外蚯舱,Hibernate的工具系列已經(jīng)擴展到包括Hibernate Search,Hibernate Validator和Hibernate OGM等流行工具阳藻,后者支持NoSQL的域模型持久性。
什么是Java ORM夹厌?
雖然它們的執(zhí)行不同或南,但每個JPA實現(xiàn)都提供某種ORM層。為了理解JPA和JPA兼容的工具,您需要掌握ORM。
對象關(guān)系映射是一項任務(wù) - 開發(fā)人員有充分的理由避免手動執(zhí)行。像Hibernate ORM或EclipseLink這樣的框架將該任務(wù)編碼為庫或框架狂巢,即ORM層斩个。作為應(yīng)用程序體系結(jié)構(gòu)的一部分叁温,ORM層負(fù)責(zé)管理軟件對象的轉(zhuǎn)換跟束,以便與關(guān)系數(shù)據(jù)庫中的表和列進行交互温学。在Java中揽祥,ORM層轉(zhuǎn)換Java類和對象站绪,以便可以在關(guān)系數(shù)據(jù)庫中存儲和管理它們敏沉。
默認(rèn)情況下辖众,持久化對象的名稱將成為表的名稱蚕键,字段將成為列频丘。設(shè)置表后怔毛,每個表行對應(yīng)于應(yīng)用程序中的對象日麸。對象映射是可配置的蛤高,但默認(rèn)值往往效果很好。
圖1說明了JPA和ORM層在應(yīng)用程序開發(fā)中的作用。
配置Java ORM層
設(shè)置新項目以使用JPA時,需要配置數(shù)據(jù)存儲區(qū)和JPA提供程序距误。您將配置數(shù)據(jù)存儲連接器以連接到您選擇的數(shù)據(jù)庫(SQL或NoSQL)寺擂。您還將包含和配置JPA提供程序,它是一個框架吝梅,如Hibernate或EclipseLink国旷。雖然您可以手動配置JPA筛欢,但許多開發(fā)人員選擇使用Spring的開箱即用支持浸锨。有關(guān)手動和基于Spring的JPA安裝和設(shè)置的演示,請參閱下面的“ JPA安裝和設(shè)置 ”版姑。
Java數(shù)據(jù)對象
Java Data Objects是一個標(biāo)準(zhǔn)化的持久性框架柱搜,它與JPA的不同之處主要在于支持對象中的持久性邏輯,以及它長期以來對使用非關(guān)系數(shù)據(jù)存儲的支持剥险。JPA和JDO足夠相似聪蘸,JDO提供者也經(jīng)常支持JPA。請參閱Apache JDO項目表制,以了解有關(guān)JDO與JPA和JDBC等其他持久性標(biāo)準(zhǔn)相關(guān)的更多信息健爬。
Java中的數(shù)據(jù)持久性
從編程的角度來看,ORM層是一個適配器層:它使對象圖的語言適應(yīng)SQL和關(guān)系表的語言么介。ORM層允許面向?qū)ο蟮拈_發(fā)人員構(gòu)建持久保存數(shù)據(jù)的軟件娜遵,而無需離開面向?qū)ο蟮姆独?/p>
使用JPA時,可以創(chuàng)建從數(shù)據(jù)存儲區(qū)到應(yīng)用程序的數(shù)據(jù)模型對象的映射夭拌。您可以定義對象和數(shù)據(jù)庫之間的映射魔熏,而不是定義對象的保存和檢索方式,然后調(diào)用JPA來保存它們鸽扁。如果您正在使用關(guān)系數(shù)據(jù)庫蒜绽,那么應(yīng)用程序代碼和數(shù)據(jù)庫之間的大部分實際連接將由JDBC(Java數(shù)據(jù)庫連接API)處理。
作為規(guī)范桶现,JPA提供元數(shù)據(jù)注釋躲雅,您可以使用它來定義對象和數(shù)據(jù)庫之間的映射。每個JPA實現(xiàn)都為JPA注釋提供了自己的引擎骡和。JPA規(guī)范還提供了PersistanceManager或者EntityManager相赁,它們是與JPA系統(tǒng)聯(lián)系的關(guān)鍵點(其中您的業(yè)務(wù)邏輯代碼告訴系統(tǒng)如何處理映射對象)。
為了使所有這些更具體慰于,請考慮清單1钮科,這是一個用于為音樂家建模的簡單數(shù)據(jù)類。
清單1. Java中的一個簡單數(shù)據(jù)類
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
類用于保存數(shù)據(jù)婆赠。它可以包含原始數(shù)據(jù)绵脯,例如名稱字段。它還可以與其他類(如mainInstrument
和performances
)保持關(guān)系。
Musician
存在的原因是包含數(shù)據(jù)蛆挫。這種類有時稱為DTO或數(shù)據(jù)傳輸對象赃承。DTO是軟件開發(fā)的常見功能。雖然它們包含多種數(shù)據(jù)悴侵,但它們不包含任何業(yè)務(wù)邏輯瞧剖。持久化數(shù)據(jù)對象是軟件開發(fā)中普遍存在的挑戰(zhàn)。
JDBC的數(shù)據(jù)持久性
將Musician
類的實例保存到關(guān)系數(shù)據(jù)庫的一種方法是使用JDBC庫可免。JDBC是一個抽象層抓于,它允許應(yīng)用程序發(fā)出SQL命令而無需考慮底層數(shù)據(jù)庫實現(xiàn)。
清單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中的代碼是相當(dāng)自我記錄的毡咏。該georgeHarrison
對象可以來自任何地方(前端提交,外部服務(wù)等)逮刨,并設(shè)置其ID和name字段呕缭。然后,對象上的字段用于提供SQL insert
語句的值修己。(PreparedStatement
該類是JDBC的一部分恢总,提供了一種將值安全地應(yīng)用于SQL查詢的方法。)
雖然JDBC允許手動配置附帶的控件睬愤,但與JPA相比片仿,它很麻煩。要修改數(shù)據(jù)庫尤辱,首先需要創(chuàng)建一個SQL查詢砂豌,該查詢從Java對象映射到關(guān)系數(shù)據(jù)庫中的表。然后光督,只要對象簽名發(fā)生更改阳距,就必須修改SQL。使用JDBC结借,維護SQL本身就成了一項任務(wù)筐摘。
JPA的數(shù)據(jù)持久性
現(xiàn)在考慮清單3,我們使用JPA 持久化Musician
類船老。
清單3.使用JPA保留George Harrison
Musician georgeHarrison = new Musician(0, "George Harrison");
musicianManager.save(georgeHarrison);
清單3用一行session.save()
替換了清單2中的手動SQL 咖熟,它指示JPA持久保存該對象。從那時起柳畔,SQL轉(zhuǎn)換由框架處理馍管,因此您永遠(yuǎn)不必離開面向?qū)ο蟮姆独?/p>
JPA中的元數(shù)據(jù)注釋
清單3中的魔力是配置的結(jié)果,該配置是使用JPA的注釋創(chuàng)建的薪韩。開發(fā)人員使用注釋來告知JPA應(yīng)該保留哪些對象咽斧,以及如何保留它們堪置。
清單4顯示了具有單個JPA注釋的Musician類。
清單4. JPA的@Entity注釋
@Entity
public class Musician {
// ..class body
}
持久對象有時稱為實體张惹。附加@Entity
到類,Musician
告知JPA應(yīng)該保留此類及其對象岭洲。
配置JPA
與大多數(shù)現(xiàn)代框架一樣宛逗,JPA 遵循約定編碼(也稱為約定優(yōu)于配置),其中框架提供基于行業(yè)最佳實踐的默認(rèn)配置盾剩。作為一個示例雷激,名為Musician
的類將默認(rèn)映射到名為Musician的數(shù)據(jù)庫表。
傳統(tǒng)配置是節(jié)省時間的告私,并且在許多情況下它運行良好屎暇。也可以自定義JPA配置。例如驻粟,您可以使用JPA的@Table
注釋來指定應(yīng)該存儲Musician類的表根悼。
清單5. JPA的@Table注釋
@Entity
@Table(name="musician")
public class Musician {
// ..class body
}
清單5告訴JPA將實體(Musician
類)持久化到musician
表中。
主鍵
在JPA中蜀撑,主鍵是用于唯一標(biāo)識數(shù)據(jù)庫中每個對象的字段挤巡。主鍵可用于引用對象并將對象與其他實體相關(guān)聯(lián)。每當(dāng)您在表中存儲對象時酷麦,您還將指定要用作其主鍵的字段矿卑。
在清單6中,我們告訴JPA要使用哪個字段作為Musician主鍵沃饶。
清單6.指定主鍵
@Entity
public class Musician {
@Id
private long id;
在這種情況下母廷,我們使用JPA的@Id
注釋將id
字段指定為Musician
主鍵。默認(rèn)情況下糊肤,此配置假定主鍵將由數(shù)據(jù)庫設(shè)置 - 例如琴昆,當(dāng)字段設(shè)置為在表上自動遞增時。
JPA支持生成對象主鍵的其他策略轩褐。它還有用于更改單個字段名稱的注釋椎咧。通常,JPA足夠靈活把介,可以適應(yīng)您可能需要的任何持久性映射勤讽。
CRUD操作
將類映射到數(shù)據(jù)庫表并建立其主鍵后,即可擁有在數(shù)據(jù)庫中創(chuàng)建拗踢,檢索脚牍,刪除和更新該類所需的一切。調(diào)用session.save()
將創(chuàng)建或更新指定的類巢墅,具體取決于主鍵字段是否為null或是否適用于現(xiàn)有實體诸狭。調(diào)用entityManager.remove()
將刪除指定的類券膀。
JPA中的實體關(guān)系
簡單地使用原始字段持久化對象只是方程式的一半。JPA還具有管理彼此相關(guān)實體的能力驯遇。在表和對象中都有四種實體關(guān)系:
- 一到多
- 許多到一
- 許多一對多
- 一比一
每種類型的關(guān)系描述了實體與其他實體的關(guān)系芹彬。例如,Musician
實體可以與由諸如List
或Set
的集合表示的實體具有一對多的關(guān)系叉庐。
如果Musician
包含一個Band
字段舒帮,這些實體之間的關(guān)系可以是多對一的,這意味著在單個Band
類上有Musician
集合 陡叠。(假設(shè)每個音樂家只在一個樂隊中演奏玩郊。)
如果Musician
包含BandMates
字段,則可以表示與其他Musician
實體的多對多關(guān)系枉阵。
最后译红,Musician
可能與Quote
實體有一對一的關(guān)系,用于表示一個著名的引語:Quote famousQuote = new Quote()
兴溜。
定義關(guān)系類型
JPA為每種關(guān)系映射類型提供注解侦厚。清單7顯示了如何注解Musician
和Performances
之間的一對多關(guān)系。
清單7.注釋一對多關(guān)系
public class Musician {
@OneToMany
@JoinColumn(name="musicianId")
private List<Performance> performances = new ArrayList<Performance>();
//...
}
需要注意的一點是@JoinColumn
告訴JPA Performance表上的哪一列將映射到Musician實體昵慌。每個performance都將與單個Musician
關(guān)聯(lián)假夺,該列由此列跟蹤。當(dāng)JPA將一個 Musician
或一個Performance
加載到數(shù)據(jù)庫中時斋攀,它將使用此信息重新構(gòu)建對象圖已卷。
在JPA中獲取策略
除了知道在數(shù)據(jù)庫中放置相關(guān)實體??的位置之外,JPA還需要知道如何加載它們淳蔼。獲取策略告訴JPA如何加載相關(guān)實體侧蘸。加載和保存對象時,JPA框架必須能夠微調(diào)對象圖的處理方式鹉梨。例如讳癌,如果Musician
類有一個bandMate
字段(如清單7所示),加載george可能導(dǎo)致整個Musician表從數(shù)據(jù)庫加載存皂!
我們需要的是定義相關(guān)實體的延遲加載的能力- 當(dāng)然晌坤,認(rèn)識到JPA中的關(guān)系可能是eager或lazy的。您可以使用注釋來自定義提取策略旦袋,但JPA的默認(rèn)配置通持璨ぃ可以直接使用,無需更改:
- 一對多:lazy
- 多對一:eager
- 多對多:lazy
- 一對一:eager
JPA安裝和設(shè)置
最后疤孕,我們將簡要介紹如何為Java應(yīng)用程序安裝和設(shè)置JPA商乎。在本演示中,我將使用EclipseLink祭阀,即JPA參考實現(xiàn)鹉戚。
安裝JPA的常用方法是在項目中包含 JPA提供程序鲜戒。清單8顯示了如何將EclipseLink作為Maven pom.xml文件中的依賴項包含在內(nèi)。
清單8.將EclipseLink包含為Maven依賴項
org.eclipse.persistence
eclipselink
2.5.0-RC1
您還需要包含數(shù)據(jù)庫的驅(qū)動程序抹凳,如清單9所示遏餐。
清單9. MySql連接器的Maven依賴關(guān)系
mysql
mysql-connector-java
5.1.32
接下來,您需要告訴系統(tǒng)您的數(shù)據(jù)庫和提供程序却桶。這在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>
還有其他方法可以向系統(tǒng)提供此信息颖系,包括以編程方式。我建議使用該persistence.xml
文件辩越,因為以這種方式存儲依賴項使得在不修改代碼的情況下更新應(yīng)用程序非常容易嘁扼。
JPA的Spring配置
使用Spring將極大地簡化JPA與應(yīng)用程序的集成。例如黔攒,將@SpringBootApplication
注釋放在應(yīng)用程序頭中會指示Spring 根據(jù)您指定的配置自動掃描類并根據(jù)需要注入EntityManager
趁啸。
如果您希望Spring為您的應(yīng)用程序提供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
結(jié)論
處理數(shù)據(jù)庫的每個應(yīng)用程序都應(yīng)該定義一個應(yīng)用程序?qū)硬桓担湮ㄒ荒康氖歉綦x持久性代碼。正如您在本文中看到的赏胚,Java Persistence API引入了一系列功能并支持Java對象持久性访娶。簡單的應(yīng)用程序可能不需要JPA的所有功能,在某些情況下觉阅,配置框架的開銷可能不值得崖疤。然而,隨著應(yīng)用程序的增長典勇,JPA的結(jié)構(gòu)和封裝確實能夠保持不變劫哼。使用JPA可以簡化目標(biāo)代碼,并提供用于訪問Java應(yīng)用程序中的數(shù)據(jù)的傳統(tǒng)框架割笙。
讀者福利:
分享免費學(xué)習(xí)資料
針對于Java程序員权烧,我這邊準(zhǔn)備免費的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)伤溉、高性能及分布式般码、Jvm性能調(diào)優(yōu)、MyBatis谈火,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構(gòu)資料)
為什么某些人會一直比你優(yōu)秀侈询,是因為他本身就很優(yōu)秀還一直在持續(xù)努力變得更優(yōu)秀,而你是不是還在滿足于現(xiàn)狀內(nèi)心在竊喜糯耍!希望讀到這的您能點個小贊和關(guān)注下我扔字,以后還會更新技術(shù)干貨囊嘉,謝謝您的支持!
資料領(lǐng)取方式:加入Java技術(shù)交流群963944895
革为,點擊加入群聊扭粱,私信管理員即可免費領(lǐng)取