企業級的軟件開發中,Java一直都是中流砥柱。在Java EE8之後,Oracle公司把企業級Java標準控制權轉交Eclipse基金。最新或·和以後的企業級Java將冠名為Jakarta EE。最新的Jakarta EE是和Java EE 8相兼容的。企業級Java標準,包含有一系列的可選且可用規範(參見下圖-1),可根據自己的業務需要酌情進行企業模塊的選擇。我們知道,任何企業應用如果不跟數據庫打交道,那都是耍流氓的。在企業級Java的標準組件中,JPA的標準和實現,無疑是非常優秀而突出的。JPA是在積累了20多年的企業級應用的行業經驗的基礎誕生的,且依然在發展中。所以說,在企業級應用開發中,與數據庫的通訊和交互,大力推薦用JPA。
JPA是一組標準和規範,是Java EE中的一塊,其最終的實現廠商各有不同(我這裡的幾篇文章,都是基於eclipselink的),但必須遵循標準。當然,各個標準的最終廠商實現,也或多或少的加入了自己的擴展和特性,可酌情考慮採用。
對於很多開發者來說,或許覺得JPA“太重”了,直接拒絕之。其實不然,JPA的基本設計和構成足夠簡單和易用。相信通過我的幾篇文章,能讓你更快更好的掌握JPA的精要,以便在實際業務中採用,讓你更多的關注業務邏輯。當然,本問首要目標是構建你的總體框架似的認知和體驗,隨著後續的示例和實戰演示,相信你會更好的理解之。
行文中,盡力長話短說,一看就懂。下面就進入正文介紹。
![Jakarta EE 之 JPA入門精要](http://p2.ttnews.xyz/loading.gif)
圖-1:企業級Java標準體系組成
1 JPA核心架構
1.1JPA核心組件
作為JAVA EE數據持久化操作的官方標準的JPA,其核心組件結構如下如所示:
![Jakarta EE 之 JPA入門精要](http://p2.ttnews.xyz/loading.gif)
圖-2:JPA組件及關係
組件構圖說明:
1)Persistence:此類javax.persistence.Persistence包含獲取EntityManagerFactory實例的的靜態幫助器方法,此方法是與供應商(JPA實現提供方)無關的,即供應商中立的方法。典型代碼示例:
EntityManagerFactory factory = Persistence.createEntityManagerFactory(persistenceUnitName);
persistenceUnitName為我們的配置文件中的持久化單元名稱。
使用Persistence類獲取EntityManagerFactory不建議經常使用以獲取工廠,因為工廠的構建是一個昂貴的操作,代價高昂。我們通常會緩存一個工廠,然後引用它重複使用,尤其在SE環境下,更應如此。此類間接需要引用persistence.xml配置文件。
2)Persistence Unit:為JPA的持久化單元配置文件,即persistence.xml。此文件中可以配置多個持久化單元,即persistence-unit元素可以根據需要配置多個。持久化單元可以認為就是persistence.xml配置文件,示例參考如下:
<code>
<persistence>
<persistence-unit>
<class>com.nd.jpa.demo.TxDepart/<class>
<class>com.nd.jpa.demo.TxEmployee/<class>
<properties>
<property>
<property> value="jdbc:mysql://localhost:33060/mytest?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
<property>
<property>
/<property>/<properties>
/<persistence-unit>
/<persistence>/<code>
EntityManagerFactory: javax.persistence.EntityManagerFactory類是實體管理器EntityManager的工廠,用於創建實體管理器,典型典型代碼如下:
<code>EntityManager em = factory.createEntityManager();//從工廠創建實體管理器/<code>
4)Persistence Contexts:作為JPA的核心術語,一個持久化單元就是一些實體類的命名配置(項)界定的範圍。持久性或持久化上下文就是指的一組受管理(託管)的實體實例集合。每個持久性上下文都與一個持久性單元相關聯,將託管實例的類限制在持久性單元定義的集合。若說一個實體實例是受管理(託管)的,意味著它包含在一個持久性上下文中,並且可以由實體管理器對其進行操作。就是因此,我們一個說實體管理器管理著一個持久性上下文,但一個持久化上下文可以對應多個實體管理器。我們可以藉由實體管理器來操作持久化上下文中的實體實例(對象)。
理解持久性上下文是理解實體管理器的關鍵。實體在持久性上下文中的包含或排除將決定對其進行的任何持久性操作的結果。如果持久性上下文參與事務,則託管實體的內存狀態將同步到數據庫。然而,儘管持久性上下文起著重要的作用,但它實際上對應用程序是不可見的。它總是通過實體管理器間接訪問,並假定在我們需要它時它就在那裡。
上面所述,結合上圖都好理解,但是持久性上下文是如何創建的?何時創建持久性上下文?實體管理器在應用中是如何體現的?這就是它開始變得有趣的地方。隨著我們JPA的深入應用,你將能更好的來理解——目前就認為它是一種邏輯性範圍管理概念即可。
5)EntityManager:javax.persistence.EntityManager是應用程序使用的主要JPA接口。每個EntityManager管理一組持久對象,並具有用於插入新對象和刪除現有對象的API。
注意,在容器之外使用時,EntityManager和EntityTransaction之間存在一對一的關係。EntityManagers還充當查詢(Query)實例的工廠。
6)Entity:表示數據(庫)存儲記錄的持久化對象。通常是用Entity註解來表示的一個POJO對象類,可對應數據庫裡的一張表。
7)EntityTransaction:前面說過,每個EntityManager與單個javax.persistence.EntityTransaction有一對一的關係。EntityTransactions允許將持久化數據上的操作分組到工作單元中,以便這些工作單元要麼完全成功,要麼完全失敗,從而使數據存儲保持原始狀態。這些全成功或全失敗操作對於維護數據完整性非常重要。通常我們在SE環境下(非Java EE環境下),要手動編程來進行實體管理器相關的事務性操作。
8)Query:javax.persistence.Query接口由每個JPA供應商實現,以查找滿足特定條件的持久對象。JPA標準支持使用Java持久性查詢語言(JPQL)和結構化查詢語言(SQL)進行查詢。編程時,我們從EntityManager獲取查詢(Query)實例。
當然,JPA中還有其它一些組件類,這裡暫不介紹。
再次強調:JPA中還有其它接口,但僅在兼容ejb3的應用程序服務器之外使用。在應用服務器中,EntityManager實例通常被注入式的,這使得手動操作EntityManagerFactory變得不必要,即不需要手動編程實現。此外,應用服務器中的事務是使用標準的應用服務器事務控件器處理的,因此,EntityTransaction也未被使用。簡而言之,若在應用服務器外應用,需要編程式操作EntityManagerFactory和事務處理。
1.1JPA配置文件
persistence.xml文件為標準化JPA運行時提供了通用的支持,並用配置元素properties和子元素property為特定廠商提供了兼容支持。JPA的第一步就是在此xml文件中配置持久化單元。此配置文件在通常在源代碼根目錄下的META-INF目錄下(若IDE會生成一個資源目錄,則位置一般為:resources\\META-INF\\persistence.xml)。這裡給出一個常規的且較詳細的persistence.xml,並作簡要解釋:
<code>
<persistence>
<persistence-unit>
<jta-data-source>jdbc/ndsa/<jta-data-source>
<provider>org.eclipse.persistence.jpa.PersistenceProvider/<provider>
<class>com.newdayedu.sa.entity.SmOrgans/<class>
<exclude-unlisted-classes>
false
/<exclude-unlisted-classes>
<properties>
<property>
/<properties>
/<persistence-unit>
/<persistence>/<code>
文件主要配置項說明:
1) persistence-unit:配置文件中,持久化單元可以根據需要配置多個,且可以是異構的數據庫;
2) persistence-unit的屬性name:為管理器工廠所有的唯一名稱;
3) persistence-unit的屬性transaction-type:有兩種類型JTA和 RESOURCE_LOCAL ;通常在Java EE服務器下選JTA,在SE下使用RESOURCE_LOCAL 。前者的事務交給服務器管理,後者一般要手動控制事務,通過EntityTransaction來進行事務的處理。
4) 子元素provider:指定JPA特定廠商的實現類,即持久化提供器。若應用中只有一種特定的提供器(一般應用服務器會提供),如無特別的元數配置要求,可以不用顯式聲明,若有多個持久化單元,且有不同的提供商來實現持久化器,則需要顯式聲明。
5) 子元素jta-data-source/ non-jta-data-source:這裡要知道,持久性單元元數據的一個基本功能屬性,是描述提供者應該從何處獲取數據庫連接(屬性)以讀寫實體數據。目標數據庫是根據位於服務器JNDI空間中的JDBC數據源的名稱指定的(服務器配置託管的數據源)。這個數據源必須是全局可訪問的,以便提供器(JPA的實現)在部署持久性應用程序時要訪問它。因此典型的情況是使用JTA事務,因此應該在jta-data-source元素中指定JTA數據源的名稱。類似地,如果持久性單元的事務類型是本地化資源的,則應該使用non-jta-data-source元素。儘管JPA定義了用於指定數據源名稱的標準元素,但它並沒有規定格式。在過去,通過在服務器特定的配置文件或管理控制檯中進行配置,數據源在JNDI中是可用的(不管JTA還是非JTA型數據源)。這個名稱並非官方可移植的,但實際上它們通常是jdbc/SomeDataSource的形式。
有些JPA(的實現)提供器通過與當前JTA事務無關的數據庫連接(非JTA連接)來提供高性能的讀取操作。然後返回查詢結果並使其與持久性上下文的內容一致。這樣提高了應用程序的可伸縮性,因為數據庫連接直到以後絕對需要時(通常是在提交時)才會參與到JTA事務中。所以為了支持這些可伸縮的讀取,除了jta-data-source元素外,還提供non-jta-data-source元素值。比如如下示例:
<code><persistence-unit>
<jta-data-source>java:app/jdbc/EmployeeDS/<jta-data-source>
<non-jta-data-source>java:app/jdbc/NonTxEmployeeDS
/<non-jta-data-source>/<persistence-unit>/<code>
請注意,EmployeeDS是一個規則配置的數據源,用於訪問employee數據庫,而NonTxEmployeeDS是一個單獨的數據源,用於訪問相同的employee數據庫,但不參與到JTA事務中。說白了,這兩個數據源都是容器相關的JNDI服務,在SE環境下,根本不用考慮這兩項配置。
6) 子元素mapping-file*:實體和可嵌入類的XML映射文件的資源名。也還可以在META-INF目錄中的一個orm.xml文件中指定映射信息。如果存在,將自動讀取orm.xml映射文件。可出現零到多個映射文件。
7) 子元素jar-file*:包含實體和可嵌入類的jar文件的名稱。JPA實現將掃描這個jar以查找相關注釋的類。可出現零到多個。
8) 子元素class*: 實體和可嵌入類的類名,可並列出現零到多個。
還有其它幾個配置元素,很少用,或說一般不用,具體就不再贅述。感興趣的可自行查看persistence元素定義。
1.1JPA應用案例
為了對上述JPA的核心構成有個更直觀的感受,我們這裡給了兩個示例作為對比總結。若一時不能理解,沒關係,繼續看完就可以掌握JPA的基本應用了。
1.1.1 SE環境下應用
下面的示例說明了JPA接口如何交互來執行JPQL查詢和更新持久化對象。該示例假設在容器外部執行(非Java EE環境)。主要示例代碼如下:
<code>public class JPADemo {
public static void main(String[] args) {
//獲取實體管理器工廠
EntityManagerFactory factory = Persistence.createEntityManagerFactory("JPADemo");
// 從工廠獲取實體管理器EntityManager
EntityManager em = factory.createEntityManager();
// 開始事務
em.getTransaction().begin();
// 查詢所有在研究部門工作,且平均每週工作超過40小時的員工
Query query = em.createQuery("SELECT e " +
" FROM Employee e " +
" WHERE e.division.name = 'Research' " +
" AND e.avgHours > 40");
List results = query.getResultList();
// 給這些努力工作的員工加薪
for (Object res : results) {
Employee emp = (Employee) res;
emp.setSalary(emp.getSalary() * 1.1);
}
// 提交(commit)將檢測所有更新了的實體,然後將它們保存在數據庫中
em.getTransaction().commit();
// 關閉實體管理器,釋放資源
em.close();
}
}/<code>
1.1.2 容器環境下應用
在容器中,EntityManager將被注入,事務將以聲明的方式處理。因此,容器內的示例本版,將完全由業務邏輯組成,實例代碼如下:
<code> @PersistenceContext(unitName = "JPADemo")
private EntityManager em;
……
// 查詢所有在研究部門工作,且平均每週工作超過40小時的員工
// -注意,這裡的實體管理器(EntityManager) em使用@PersistenceContext
// 或@Resource註解注入的,其創建由應用服務器完成,這裡直接引用
Query query = em.createQuery("select e from Employee e where "
+ "e.division.name = 'Research' and e.avgHours > 40");
List results = query.getResultList();
// 給努力工作的員工加薪,事務有容器管理
for (Object res : results) {
emp = (Employee) res;
emp.setSalary(emp.getSalary() * 1.1);
}
……/<code>
在前兩個應用示例中,對於應用程序管理的實體管理器,在Java SE和Java EE環境之間的區別,不在於如何創建實體管理器,而在於如何獲得工廠。
本篇內容到此介紹,請一定仔細看看,理解JPA的總體性機制,後續在慢慢深入,若一下就深入細節,結果就是讓你很想放棄。
下一篇我會介紹JPA的核心接口以及相關API。在此係列介紹後,我會在給出一個完整的JPA工程,以供你進一步實戰操練。
敬請期待吧 ^_^
歡迎點贊、收藏、轉發;若能打賞一二,那一準會點燃我更多的創作激情的 ^_^
閱讀更多 牛旦教育IT課堂 的文章