日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

結(jié)合SPRING 2.0使用JAVA PERSISTENCE API

 WindySky 2007-01-30
結(jié)合SPRING 2.0使用JAVA PERSISTENCE API

時(shí)間:2006-05-16
作者:Seth White
瀏覽次數(shù): 2654
本文關(guān)鍵字:open jpajpajava persistence apispringmedrecpojoPersistenceWebLogic ServerSeth White持久化
文章工具
推薦給朋友 推薦給朋友
打印文章 打印文章

摘要

  Java Persistence API(JPA)和Spring Framework的2.0版本已經(jīng)引起開(kāi)發(fā)人員廣泛的興趣。本文將考察如何將Spring 2.0和JPA與BEA WebLogic Server一起使用。具體來(lái)說(shuō),我們將使用Spring和JPA對(duì)WebLogic Server的病歷示例應(yīng)用程序進(jìn)行更新。本文將展示Spring和JPA這個(gè)功能強(qiáng)大的組合如何形成基于POJO的簡(jiǎn)化應(yīng)用架構(gòu)的基礎(chǔ)。使用的技術(shù)主要包括WebLogic Server 9.1、Spring 2.0和Kodo JPA。

簡(jiǎn)介

  病歷示例應(yīng)用程序(MedRec)是一個(gè)綜合應(yīng)用程序,它演示了如何在WebLogic Server中使用多種技術(shù)。這些技術(shù)包括開(kāi)源技術(shù)(如Spring和Struts)以及WebLogic Server所使用的技術(shù)(如Web services、JSP、消息驅(qū)動(dòng)bean和JDBC)。

   本文描述在MedRec更新版本中Java Persistence API(JPA)是如何與Spring Framework配合使用的。本文的一個(gè)重要目的是向開(kāi)發(fā)人員展示Spring 2.0、WebLogic Server 9.1和Kodo 4.0如何一起使用。通過(guò)閱讀本文,開(kāi)發(fā)人員將獲得對(duì)有關(guān)使用JPA和Spring 2.0版本中新增的JPA支持的了解。本文還將討論在企業(yè)應(yīng)用的多個(gè)層次中重用JavaBean(POJO)時(shí)可能出現(xiàn)的挑戰(zhàn)。重用是基于Spring和JPA的應(yīng)用架構(gòu)所帶來(lái)的重要獲益之一。

   對(duì)于不熟悉Java Persistence API的人來(lái)說(shuō),JPA是一個(gè)指定如何將Java對(duì)象保存在數(shù)據(jù)庫(kù)中的新增簡(jiǎn)化API。JPA正在作為EJB 3.0(JSR 220)的一部分而被開(kāi)發(fā),因?yàn)樗鼘⑷〈鶨JB 2.x實(shí)體bean,但是它在J2EE和J2SE應(yīng)用程序中均可使用。JPA的一個(gè)最重要的特性是:它是基于POJO的。JPA還使用Java 5.0注釋來(lái)簡(jiǎn)化指定從Java對(duì)象到關(guān)系數(shù)據(jù)庫(kù)的映射的方式。BEA已經(jīng)宣告建立OpenJPA(一個(gè)很快就可用的基于Kodo的開(kāi)源項(xiàng)目),但是您現(xiàn)在就可以開(kāi)始使用Kodo 4.0的早期訪問(wèn)版本。

   本文將從對(duì)Spring中的POJO和數(shù)據(jù)訪問(wèn)的概述開(kāi)始。接下來(lái)概述MedRec的架構(gòu),并對(duì)MedRec數(shù)據(jù)訪問(wèn)層進(jìn)行更詳細(xì)的描述。然后我們將詳細(xì)考察JPA persistent類,并討論它們需要遵循的設(shè)計(jì)模式。最后,我們將介紹Spring和Kodo JPA的XML配置,以便將所有內(nèi)容聯(lián)系起來(lái)。MedRec的完整源代碼可隨本文下載。

Spring中的POJO和數(shù)據(jù)訪問(wèn)

  Spring Framework最著名的優(yōu)點(diǎn)可能是簡(jiǎn)化了的業(yè)務(wù)組件開(kāi)發(fā)。熟悉Spring的人都知道,通過(guò)使用反向控制(IoC)和面向方面編程(AOP),Spring允許開(kāi)發(fā)人員編寫(xiě)功能強(qiáng)大的業(yè)務(wù)組件,這些業(yè)務(wù)組件仍然是常規(guī)JavaBean或POJO (Plain Old Java Object)——正如它們通常被稱作的那樣。

   對(duì)于需要訪問(wèn)數(shù)據(jù)庫(kù)的企業(yè)應(yīng)用程序,Spring也提供了一個(gè)框架來(lái)簡(jiǎn)化對(duì)封裝持久化數(shù)據(jù)訪問(wèn)的數(shù)據(jù)訪問(wèn)對(duì)象(DAO)的創(chuàng)建。Spring的POJO功能還涉及數(shù)據(jù)訪問(wèn)領(lǐng)域,因?yàn)樗鼈儾樵兒托薷牡臄?shù)據(jù)訪問(wèn)對(duì)象和域模型對(duì)象均可以是POJO。

   Spring對(duì)POJO模式的使用具有一些重要的實(shí)際優(yōu)點(diǎn)。首先,就業(yè)務(wù)組件而言,POJO減輕了開(kāi)發(fā)人員為實(shí)現(xiàn)應(yīng)用程序而必須做的工作。它不僅減少了代碼行,而且由于這些代碼本身就是標(biāo)準(zhǔn)Java,所以它們更簡(jiǎn)單。其次,使用POJO意味著應(yīng)用程序調(diào)用數(shù)據(jù)訪問(wèn)對(duì)象的其余代碼不依賴于特定的持久化技術(shù)。例如,如果應(yīng)用程序正在使用原始JDBC或JDO,那么使用POJO則會(huì)使轉(zhuǎn)換到使用JPA作為持久化解決方案的行為相對(duì)容易。

   第三個(gè)優(yōu)點(diǎn)涉及域模型對(duì)象本身。在MedRec中,域模型對(duì)象代表諸如病人、醫(yī)生和處方之類的實(shí)體。由于這些類是POJO,而且不像傳統(tǒng)的EJB 2.x實(shí)體bean那樣依賴于特定的持久化環(huán)境,所以這些類可以在多個(gè)應(yīng)用程序代碼需要訪問(wèn)域模型的層次中重用。在MedRec中,這包括JSP使用域模型對(duì)象呈現(xiàn)用戶界面的Web層,以及使用域模型類作為參數(shù)并將類型返回給Web服務(wù)方法的Web服務(wù)層。

MedRec概覽

  如果希望全面概覽MedRec應(yīng)用程序的架構(gòu)及其使用Spring的方式,請(qǐng)閱讀Spring Integration with WebLogic Server(中文版,dev2dev,2006年3月)。這篇文章很好地描述了Spring和WebLogic Server之間的常規(guī)集成。這里我們將簡(jiǎn)要討論MedRec架構(gòu),以說(shuō)明JPA是如何用于MedRec環(huán)境的。

   圖1展示了MedRec的整體架構(gòu)。在最高層,MedRec實(shí)際上是兩個(gè)獨(dú)立的J2EE應(yīng)用(EAR文件):MedRec應(yīng)用程序和Physician應(yīng)用程序。所有的數(shù)據(jù)訪問(wèn)都由MedRec部分完成,所以我們將關(guān)注這部分。

MedRec概覽
圖1:MedRec應(yīng)用程序架構(gòu)

   如圖1所示,MedRec應(yīng)用程序分為幾個(gè)層:一個(gè)包含Web服務(wù)的表示層、一個(gè)服務(wù)層、一個(gè)包括數(shù)據(jù)訪問(wèn)的集成層以及數(shù)據(jù)庫(kù)層。作為集成層一部分的Spring數(shù)據(jù)訪問(wèn)對(duì)象(DAO)主要由服務(wù)層的業(yè)務(wù)對(duì)象調(diào)用。DAO和服務(wù)對(duì)象都是由Spring應(yīng)用程序上下文配置的Spring bean。服務(wù)對(duì)象使用Spring的聲明式事務(wù)支持來(lái)控制事務(wù)。

MedRec數(shù)據(jù)訪問(wèn)

  Medical Records應(yīng)用程序使用4種不同的DAO,每一種對(duì)應(yīng)于一個(gè)需要保存的域模型類——Patient、Prescription、Record和User。DAO實(shí)現(xiàn)類分別被稱為JpaPatientDAO、JpaPrescriptionDAO、JpaRecordDAO和JpaUserDAO,可以在com.bea.medrec.dao.orm包中找到。

   讓我們更詳細(xì)地考察Patient DAO,以便弄清楚它是如何工作的。以下是用于處理病歷應(yīng)用程序中病人數(shù)據(jù)的檢索和更新的數(shù)據(jù)訪問(wèn)對(duì)象接口:

public interface PatientDao {
public Patient getById(Integer patientId)
throws DataAccessException;
public List getByEmail(String email)
throws DataAccessException;
public List getByLastName(String lastName)
throws DataAccessException;
public List getByLastNameFirstName(String lastName,
String firstName) throws DataAccessException;
public List getByLastNameWild(String lastName)
throws DataAccessException;
public Patient getBySsn(String ssn)
throws DataAccessException;
public List getByStatus(String status)
throws DataAccessException;
public Patient getByUsername(String username)
throws DataAccessException;
public Patient save(Patient patient)
throws DataAccessException;
public Patient update(Patient patient)
throws DataAccessException;
}

  請(qǐng)注意,Patient DAO接口是plain old Java interface(POJI)。它不擴(kuò)展或?qū)肴魏翁囟ㄓ谒褂玫某志没夹g(shù)(本例中是JPA)的Java類型。大多數(shù)Patient DAO方法執(zhí)行查詢并返回病人列表或單個(gè)病人對(duì)的。最后兩個(gè)方法分別用于在數(shù)據(jù)庫(kù)中保存新病人的信息或者更新現(xiàn)有病人的信息。

   該接口上的每個(gè)方法都聲明了一個(gè)DataAccessException,它是由Spring framework定義的運(yùn)行時(shí)異常。關(guān)于DataAccessException,有兩件事需要注意。首先,它是運(yùn)行時(shí)異常,所以使用數(shù)據(jù)訪問(wèn)對(duì)象的應(yīng)用程序代碼不需要像在JDBC和EJB 2.x實(shí)體bean的情況下那樣使用try-catch塊包裝每次調(diào)用。第二,DataAccessException是有用的,因?yàn)樗b底層持久化技術(shù)所使用的特定異常類,從而使應(yīng)用程序的其他部分獨(dú)立于持久化層。

   關(guān)于接口就說(shuō)這么多了。接下來(lái),讓我們考察一下Patient DAO的實(shí)現(xiàn)。以下是基類BaseDAO,MedRec中的所有數(shù)據(jù)訪問(wèn)對(duì)象都對(duì)它進(jìn)行擴(kuò)展:

package com.bea.medrec.dao.orm;
import org.springframework.orm.jpa.support.JpaDaoSupport;
public abstract class BaseDao extends JpaDaoSupport {
...
}

  在我們的例子中,這是一個(gè)非常簡(jiǎn)單的類,但是一般來(lái)說(shuō),它可用于在不同數(shù)據(jù)訪問(wèn)對(duì)象實(shí)現(xiàn)上所共有的任何代碼。BaseDAO擴(kuò)展了Spring的JpaDaoSupport類。這是必須的,因?yàn)樗鼮槲覀兊腄AO實(shí)現(xiàn)類提供到Spring的特定于JPA的API的訪問(wèn)。

   Patient DAO實(shí)現(xiàn)類JpaPatientDao擴(kuò)展了BaseDao:

public class JpaPatientDao extends BaseDao implements PatientDao {
public Patient getById(Integer pId)
throws DataAccessException {
return getJpaTemplate().find(Patient.class, pId);
}
public List getByLastName(String pLastName)
throws DataAccessException {
List patients = getJpaTemplate().find(
"SELECT p " +
"FROM " + Patient.class.getSimpleName() + " p " +
"WHERE p.lastName LIKE ?1", pLastName);
return (patients.isEmpty())?
Collections.EMPTY_LIST : patients;
}
...
}

  此代碼示例還展示了兩個(gè)示范查詢方法實(shí)現(xiàn)。第一個(gè)是getById(),它通過(guò)病人的惟一標(biāo)識(shí)字段查找病人。這是通過(guò)調(diào)用Spring的JpaTemplate上的專用find方法完成的,它將Patient類和惟一的id作為參數(shù),并返回具有此id的Patient對(duì)象。getByLastName()方法返回具有相似的姓的病人列表。getByLastName()使用JpaTemplate.find()的替代版本,它將后跟查詢參數(shù)的EJB QL查詢作為參數(shù),并返回匹配查詢的對(duì)象列表。以下是patient DAO的保存和更新方法:

public class JpaPatientDao extends BaseDao implements PatientDao {
...
public Patient save(Patient pPatient)
throws DataAccessException {
getJpaTemplate().persist(pPatient);
return pPatient;
}
public Patient update(Patient pPatient)
throws DataAccessException {
return getJpaTemplate().merge(pPatient);
}
}

  在此patient DAO的保存和更新方法的實(shí)現(xiàn)中,save()方法用于將新病人的記錄插入數(shù)據(jù)庫(kù),而update()則修改現(xiàn)有的病人記錄。每個(gè)方法返回一個(gè)對(duì)新的或更新后的Patient對(duì)象的引用。重點(diǎn)是要理解保存和合并不會(huì)立即導(dǎo)致數(shù)據(jù)庫(kù)更新。更改通常由JPA緩存直到當(dāng)前事務(wù)提交。然后更改被批量發(fā)送到數(shù)據(jù)庫(kù),但是只要有查詢?cè)谶\(yùn)行,未決更改也可以常常被刷新。

   請(qǐng)注意一件有趣的事,我們的Patient DAO對(duì)象對(duì)JPA API的依賴很少,因?yàn)镾pring模板被用于所有數(shù)據(jù)訪問(wèn)操作,它又內(nèi)部委托給JPA。事實(shí)上,到目前為止,我們看到的對(duì)JPA的惟一直接依賴是getByLastName()方法使用的EJB QL查詢字符串。還請(qǐng)注意,雖然早些時(shí)候我們將Spring DAO稱作POJO,但是這并不完全正確,因?yàn)镈AO需要擴(kuò)展Spring的JpaDaoSupport類。然而,重要的是,暴露給應(yīng)用程序其他部分的接口DAO是不依賴于JPA或Spring類型的plain old Java interface。

JPA持久化類

  到目前為止,我們已經(jīng)看到一些處理查詢和更新的示例數(shù)據(jù)訪問(wèn)代碼,那么持久化類又是什么呢?想想POJO和元數(shù)據(jù)。但是,持久化POJO需要遵循許多規(guī)則,或者說(shuō)是設(shè)計(jì)模式。這些規(guī)則部分由JPA指定,而其他的是根據(jù)整個(gè)應(yīng)用架構(gòu)的要求而產(chǎn)生的,正如我們下面將看到的。

   首先是一條關(guān)于元數(shù)據(jù)的注意事項(xiàng)。JPA為開(kāi)發(fā)人員提供了幾種選項(xiàng)來(lái)指定與持久化和對(duì)象-關(guān)系映射相關(guān)的JPA元數(shù)據(jù):在外部XML文件中指定、在Java 5.0注釋中指定,或者使用這二者的組合。病歷應(yīng)用程序使用JPA元數(shù)據(jù)的Java注釋。這樣做的好處是:元數(shù)據(jù)與應(yīng)用該元數(shù)據(jù)的Java類、字段和方法一起配置,這樣可以幫助理解。如果想使元數(shù)據(jù)與代碼分離,可以使用XML來(lái)代替。

   讓我們考察一下Patient類及其字段聲明,看看示例注釋JPA類是什么樣的:

package com.bea.medrec.domain;
import javax.persistence.*;
import kodo.persistence.jdbc.ElementJoinColumn;
@Entity()
public class Patient implements Serializable
{
@Id()
@GeneratedValue(strategy=IDENTITY)
private Integer id;
private Date dob;
@Column(name = "first_name")
private String firstName;
private String gender;
@Column(name = "last_name")
private String lastName;
@Column(name = "middle_name")
private String middleName;
private String phone;
private String ssn;
@ManyToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="address_id")
private Address address;
@OneToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="email")
private User user;
@OneToMany(tar on getEntity = Prescription.class)
@ElementJoinColumn(name="pat_id")
private Set prescriptions=null;
@OneToMany (targetEntity=Record.class)
@ElementJoinColumn(name="pat_id")
private Set records =null;
...
}

  請(qǐng)注意,Patient類使用Entity注釋進(jìn)行注釋。這是使用注釋的所有持久化類必須遵循的。Entity注釋的存在告訴持久化引擎該類是JPA實(shí)體。通常不要求Entity類是公有的,但是MedRec的持久化類需要是公有的,以便Web和Web服務(wù)層可以使用它們。

   實(shí)體類還需要實(shí)現(xiàn)Serializable,但是由于Patient類由MedRec struts操作置于HTTP會(huì)話中,所以它需要是可串行化的。這樣應(yīng)用程序可以在集群環(huán)境中工作,其中會(huì)話狀態(tài)可以在集群中各節(jié)點(diǎn)之間進(jìn)行串行化。關(guān)于實(shí)體類的其他要求是:它必須是頂級(jí)類且不是final。請(qǐng)參閱JPA規(guī)范(JSR220)以獲得完整的規(guī)則列表。

持久化字段

  JPA實(shí)現(xiàn)需要在運(yùn)行時(shí)讀取和寫(xiě)入實(shí)體類的狀態(tài)(持久化字段)。JPA規(guī)范允許實(shí)現(xiàn)或者通過(guò)直接訪問(wèn)實(shí)體字段的方式或者通過(guò)調(diào)用JavaBean風(fēng)格的accessor方法(getter/setter)的方式來(lái)進(jìn)行此操作。使用的訪問(wèn)方式由持久化注釋的位置決定。例如,如果注釋位于類的字段上,則使用字段訪問(wèn)。

   您可能要問(wèn):為什么JPA支持字段和方法兩種訪問(wèn)方式呢?答案是靈活性。一些類在它們的公有accessor方法中執(zhí)行驗(yàn)證。如果由于數(shù)據(jù)庫(kù)中的值溢出而拋出異常,這可能導(dǎo)致JPA實(shí)現(xiàn)出現(xiàn)問(wèn)題。如果類在其setter中執(zhí)行驗(yàn)證,則最好對(duì)字段進(jìn)行注釋。另一方面,如果希望支持虛擬持久化屬性之類的功能,那么就注釋accessor方法。

   在JPA實(shí)體中,所有未使用Transient注釋(由JPA定義)進(jìn)行注釋的非瞬態(tài)字段都是持久化字段。“持久化”意味著字段被映射到數(shù)據(jù)庫(kù)中的列。請(qǐng)注意,Patient類中的一些持久化字段沒(méi)有注釋。這是由于JPA定義的默認(rèn)值(如默認(rèn)的列名稱)對(duì)于這些字段來(lái)說(shuō)是正確的。如果字段名稱與其映射的數(shù)據(jù)庫(kù)列名稱不同,則必須使用Column注釋來(lái)為數(shù)據(jù)庫(kù)列指定一個(gè)不同的列名稱。

   每個(gè)實(shí)體必須具有主鍵。JPA支持簡(jiǎn)單(單字段)和復(fù)合(多字段)的主鍵。如果可以,最好一直使用簡(jiǎn)單主鍵,并讓數(shù)據(jù)庫(kù)生成主鍵值。這是因?yàn)?,一旦?duì)象被保存,JPA就不允許更改主鍵值。由于此原因,病歷應(yīng)用程序中的大多數(shù)持久化類(包括Patient類)都使用簡(jiǎn)單的生成主鍵。關(guān)于具有復(fù)合主鍵的類的示例,請(qǐng)參考Group類。Id注釋用于標(biāo)記主鍵字段或標(biāo)識(shí)字段。

   GeneratedValue注釋用于表示在插入新的Patient時(shí)主鍵值是由數(shù)據(jù)庫(kù)生成的。數(shù)據(jù)庫(kù)可以以數(shù)種方式生成主鍵值,可使用strategy屬性來(lái)選擇其中一個(gè)。Patient類使用標(biāo)識(shí)策略,這意味著id字段應(yīng)被映射到數(shù)據(jù)庫(kù)自動(dòng)-增量或標(biāo)識(shí)列。

   在使用標(biāo)識(shí)策略時(shí),需要注意這對(duì)類的equals和hashcode方法所產(chǎn)生的影響。例如,如果equals方法比較id字段的值,請(qǐng)確保使用對(duì)象的方式不會(huì)導(dǎo)致equals方法在進(jìn)行數(shù)據(jù)庫(kù)插入操作之前(通常在事務(wù)提交時(shí))被調(diào)用,因?yàn)橹麈I值直到這時(shí)才被分配。避免這種情況的一種方法是,(如果類有的話)在equals方法中使用自然鍵而不是生成鍵。例如,Patient Social Security number是可用于Patient equals方法中的很好的自然鍵。

關(guān)系

  大多數(shù)實(shí)體具有與域模型中其他的單個(gè)或多個(gè)實(shí)體的關(guān)系,Patient類也不例外。圖2展示了Patient類及其關(guān)系圖。

Patient類及其關(guān)系圖
圖2.Patient類關(guān)系

   JPA使用一對(duì)一、多對(duì)一、一對(duì)多和多對(duì)多注釋來(lái)指定關(guān)系及其基數(shù)。每個(gè)關(guān)系字段必須使用這些注釋中的一個(gè)進(jìn)行注釋。例如,Patient具有與Address的多對(duì)一關(guān)系。這意味著許多病人可能具有相同的地址。Patient與User類具有一對(duì)一關(guān)系,該類包含病人的用戶名和密碼信息。這意味著每個(gè)Patient與單個(gè)的User相關(guān)。

   Patient具有與Prescriptions和Records的一對(duì)多關(guān)系,因?yàn)樘囟ǖ牟∪丝梢杂卸鄠€(gè)處方,并可能多次看醫(yī)生(這將導(dǎo)致創(chuàng)建病歷)。相反,每個(gè)Prescription或Record只可以與一個(gè)Patient關(guān)聯(lián)。

級(jí)聯(lián)操作

  JPA關(guān)系的一個(gè)有趣特性是操作的級(jí)聯(lián)。您可能已經(jīng)熟悉了大多數(shù)關(guān)系數(shù)據(jù)庫(kù)所支持的“級(jí)聯(lián)刪除”特性。JPA采用了類似于刪除的級(jí)聯(lián)操作的理念,并將其應(yīng)用于其他操作(例如插入和更新)。Patient類將插入和關(guān)系操作級(jí)聯(lián)到相關(guān)的Address和User。在JPA中插入被視為持久化,因?yàn)閿?shù)據(jù)庫(kù)插入是在對(duì)對(duì)象持久化時(shí)執(zhí)行的。更新被視作合并,因?yàn)楦聦?duì)象中的更改合并到當(dāng)前JPA持久化上下文中。這看上去有些混亂,但是請(qǐng)別擔(dān)心;只需記住:在希望更新已經(jīng)被保存的對(duì)象時(shí),要調(diào)用合并;當(dāng)要在數(shù)據(jù)庫(kù)中插入新的持久化對(duì)象時(shí),要調(diào)用持久化。

   確定是否對(duì)特定操作(例如持久化、合并或移除)進(jìn)行級(jí)聯(lián)是需要技巧的。在Patient類的情況中,持久化被級(jí)聯(lián)到Address和User,因?yàn)閼?yīng)用程序也需要在插入Patient的地方插入新的Address和User。類似地,在更新Patient的時(shí)候,Address和User可能也需要更新。Prescriptions和Records則不同。應(yīng)用程序具有顯式持久化(以及更新)Prescription或Record的操作,所以不需要將這些操作從Patient級(jí)聯(lián)到這些類。

  您可能還記得前一節(jié)中我們?cè)f(shuō)多個(gè)Patient被允許引用相同的Address——從Patient到Address的關(guān)系是多對(duì)一。但是,我們剛剛也說(shuō)了Patient將插入級(jí)聯(lián)到Address,這導(dǎo)致為數(shù)據(jù)庫(kù)中的每位病人創(chuàng)建一個(gè)新地址。這是怎么回事呢?您在這里看到的是在Patient和Address之間使用生成的主鍵和級(jí)聯(lián)插入的有趣效果。因?yàn)橛糜诘刂返闹麈I由數(shù)據(jù)庫(kù)自動(dòng)生成,所以每個(gè)Address將有惟一的主鍵,指向相同實(shí)際地址的兩個(gè)不同Address對(duì)象有可能都被插入到數(shù)據(jù)庫(kù)中。這對(duì)于許多應(yīng)用程序(包括MedRec)來(lái)說(shuō)沒(méi)什么問(wèn)題,但是如果您希望阻止復(fù)制這樣的地址,則DAO需要執(zhí)行一些附加的邏輯。

Web Services注意事項(xiàng)

  JPA將MedRec的實(shí)體類映射到數(shù)據(jù)庫(kù)模式,而WebLogic Server 9.1 Web services實(shí)現(xiàn)將其映射到XML,這引發(fā)了一些影響實(shí)體類設(shè)計(jì)的其他問(wèn)題。

   JPA支持單向和雙向關(guān)系,但是如果考察MedRec中的實(shí)體類,會(huì)看到所有關(guān)系都是單向的。這是必要的,因?yàn)閃eb services綁定不支持雙向關(guān)系。

   由于JPA要求Java Collection類型(如java.util.Set)用于多值關(guān)系字段,這引發(fā)了另一個(gè)問(wèn)題。實(shí)踐表明9.1中的Web services不支持Collection類型。那么我們?nèi)绾谓鉀Q這一問(wèn)題呢?

   不知您是否還記得,對(duì)象-關(guān)系映射可以在實(shí)體的字段或方法上定義。實(shí)踐表明,JPA的此靈活性非常重要。另一方面,Web services使用類的公有屬性accessor方法(getXXX()方法)定義被封送為XML的屬性。通過(guò)使用字段定義持久化映射以及由類上的方法定義XML綁定,我們能夠?qū)蓚€(gè)映射分離!多值關(guān)系為了持久化在內(nèi)部使用Set,但是可以通過(guò)屬性訪問(wèn)方法將其暴露為XML綁定能夠處理的數(shù)組。

   為了具體說(shuō)明,下面是一個(gè)來(lái)自Record類的執(zhí)行此轉(zhuǎn)換的屬性訪問(wèn)方法示例:

public Prescription[] getPrescriptions() {
if (prescriptions == null)
return null;
return (Prescription[]) prescriptions.toArray(new
Prescription[prescriptions.size()]);
}

  此方法在內(nèi)部訪問(wèn)具有java.util.Set類型的持久化處方字段。此Set被轉(zhuǎn)換為Web services實(shí)現(xiàn)能夠處理的數(shù)組。關(guān)于Web services集成的體驗(yàn)使我們對(duì)JPA及其與現(xiàn)有應(yīng)用程序和基礎(chǔ)架構(gòu)集成的能力印象深刻。

   現(xiàn)在對(duì)持久化類的討論就結(jié)束了??雌饋?lái)似乎需要遵循許多規(guī)則和限制,但事實(shí)上大多數(shù)JPA只是將好的編碼實(shí)踐具體化了,您可能會(huì)發(fā)現(xiàn)您沒(méi)有過(guò)多考慮就在遵循它們。

使用DAO實(shí)現(xiàn)服務(wù)

  現(xiàn)在我們看到了如何使用Spring 2.0和JPA實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)對(duì)象,接下來(lái)讓我們快速考察一下使用DAO的MedRec服務(wù)對(duì)象。以下的代碼示例顯示了PatientServiceImpl類及其業(yè)務(wù)方法之一,processNewRegistration方法:

public class PatientServiceImpl
extends BaseService implements PatientService {
protected PatientDao patientDao;
public void setPatientDao(PatientDao patientDao) {
this.patientDao = patientDao;
}
public void processNewRegistration(Registration registration) {
try {
User user = registration.getUser();
Patient patient = registration.getPatient();
patient.setUser(user);
user.setStatus(MedRecConstants.USER_NEW);
patientDao.save(pPatient);
} catch (Exception ex) {
...
}
}
...
}

  當(dāng)新病人使用MedRec Web接口自我注冊(cè)時(shí)會(huì)調(diào)用processNewRegistration方法。它將保存新病人注冊(cè)信息的Registration對(duì)象作為參數(shù)。ProcessNewRegistration首先從Registration對(duì)象獲取Patient和User,并初始化它們之間的關(guān)系。User對(duì)象包含病人的用戶名和密碼信息,還保存被設(shè)置為USER_NEW的病人狀態(tài),因?yàn)檫@是位必須由管理員批準(zhǔn)的新病人。然后調(diào)用Patient DAO將新的病人插入到數(shù)據(jù)庫(kù)。

   針對(duì)MedRec服務(wù)對(duì)象對(duì)Spring聲明式事務(wù)進(jìn)行配置,以便在調(diào)用業(yè)務(wù)方法(如processNewRegistration)之前由Spring啟動(dòng)事務(wù),并在此方法完成時(shí)提交該事務(wù)。這確保了DAO作為可自動(dòng)提交的現(xiàn)有事務(wù)的一部分而運(yùn)行。還請(qǐng)注意Spring所使用的、將對(duì)DAO對(duì)象的引用注入服務(wù)bean中的setPatientDao方法。

配置

  到目前為止,我們已經(jīng)考察了一些構(gòu)成此版本的病歷應(yīng)用程序的Java代碼。本節(jié)將為您介紹Spring和Kodo要求的外部配置。我們從Spring開(kāi)始。

Spring配置

  我們使用一組模塊化XML配置文件來(lái)配置Spring ApplicationContext。包含數(shù)據(jù)訪問(wèn)對(duì)象配置的配置文件是applicationContext-orm.xml,它位于MedRec安裝目錄下的src\medrecEar\APP-INF\classes目錄中。以下是applicationContext-orm.xml中聲明Patient DAO的片段:

<bean id="patientDao"
class="com.bea.medrec.dao.orm.JpaPatientDao"
autowire="byType"/>

  Spring的JPA DAO具有需要被注入的單一依賴,即JPA EntityManagerFactory。DAO使用此EntityManagerFactory創(chuàng)建執(zhí)行實(shí)際持久化工作的JPA EntityManagers。這里使用Spring自動(dòng)綁定特性完成綁定,這就是為什么我們?cè)赬ML中看不到對(duì)EntityManagerFactory的顯式依賴。自動(dòng)綁定特性自動(dòng)將DAO綁定到基于DAO的EntityManagerFactory屬性(繼承自所有MedRec DAO類對(duì)其進(jìn)行擴(kuò)展的Spring JpaDaoSupport類)類型的EntityManagerFactory。

   以下代碼是聲明創(chuàng)建EntityManagerFactory的Spring工廠bean的片段。由于這里討論了兩個(gè)工廠,可能聽(tīng)起來(lái)有些混亂,但是請(qǐng)記住:Spring工廠bean的目的就是提供一個(gè)中間層,以便允許我們執(zhí)行某些自定義代碼以創(chuàng)建EntityManagerFactory。此EntityManagerFactory是DAO對(duì)象惟一知道或關(guān)心的工廠。

<bean id="entityManagerFactory"
class="com.bea.medrec.utils.KodoEntityManagerFactoryBean">
<property name="jndiName">
<value>kodo-jpa</value>
</property>
</bean>

  因?yàn)樵谧珜?xiě)本文的時(shí)候,WebLogic Server不支持與JPA的標(biāo)準(zhǔn)集成,所以EntityManagerFactory通過(guò)使用自定義工廠bean而創(chuàng)建。EntityManagerFactory需要知道在JNDI中的什么地方查找Kodo資源適配器。該信息被配置為Spring工廠bean上的屬性,后者將其傳送到EntityManagerFactory。請(qǐng)記住,工廠bean的任務(wù)是創(chuàng)建EntityManagerFactory事件并將其返回到Spring,以使Spring能夠?qū)ntityManagerFactory注入DAO bean。用于創(chuàng)建實(shí)體管理器工廠實(shí)例的工廠bean代碼如下所示:

public class KodoEntityManagerFactoryBean
implements FactoryBean, InitializingBean {
private String jndiName = "kodo-jpa";
private EntityManagerFactory entityManagerFactory = null;
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
public Object getObject() throws Exception {
return entityManagerFactory;
}
public Class getObjectType() {
return EntityManagerFactory.class;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() throws Exception {
try {
entityManagerFactory  =
KodoPersistence.createEntityManagerFactory(
jndiName, new InitialContext());
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}

  KodoEntityManagerFactoryBean是一個(gè)相當(dāng)簡(jiǎn)單的Spring工廠bean。在運(yùn)行時(shí),Spring首先調(diào)用默認(rèn)的構(gòu)造函數(shù)創(chuàng)建bean實(shí)例。然后,Spring調(diào)用setJndiName()方法設(shè)置Kodo資源適配器的JNDI名稱。一旦所有屬性被注入,Spring就調(diào)用創(chuàng)建EntityManagerFactory實(shí)例的afterPropertiesSet()。最后,Spring調(diào)用getObject()來(lái)獲取將被注入DAO的EntityManagerFactory。

   我們已經(jīng)看到了如何使用Kodo EntityManagerFactory(DAO使用它來(lái)完成所有持久化工作)注入Spring DAO。然后Kodo EntityManagerFactory通過(guò)JNDI連接到Kodo資源適配器。接下來(lái),我們將考察Kodo資源適配器的配置,但是首先需要知道一點(diǎn)關(guān)于Spring配置的其他內(nèi)容,這將有助于將所有事情聯(lián)系起來(lái)。

   Spring需要被告知使用WebLogic JTA事務(wù)管理器來(lái)啟動(dòng)和提交事務(wù)。此操作在以下的XML片段中完成:

<bean id="transactionManager"
class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="transactionManagerName"
value="javax.transaction.TransactionManager"/>
</bean>

  這里要明白一件重要的事情,即,需要專門告訴Spring將事務(wù)工作委托給WebLogic JTA實(shí)現(xiàn)。稍后,我們將看到Kodo也被告知在其配置中使用JTA事務(wù)。這將安排好事情以便MedRec服務(wù)對(duì)象啟動(dòng)WebLogic JTA事務(wù),然后在此事務(wù)中調(diào)用DAO。隨后DAO調(diào)用Kodo,它作為現(xiàn)有WebLogic JTA事務(wù)的一部分執(zhí)行數(shù)據(jù)庫(kù)讀出和寫(xiě)入。

Kodo配置

  我們使用Kodo 4.0 EA4,它是Kodo的JPA實(shí)現(xiàn)的早期訪問(wèn)版本。在撰寫(xiě)本文的時(shí)候,定義JPA的EJB 3.0規(guī)范尚未完成,所以Kodo支持JPA的pre-final版本。Kodo作為資源適配器被部署到WebLogic Server。此資源適配器在JNDI中進(jìn)行注冊(cè)。資源適配器的配置在標(biāo)準(zhǔn)ra.xml描述符文件中進(jìn)行,它位于資源適配器RAR文件的META-INF目錄中。

   如果考察ra.xml文件,會(huì)看到Kodo支持多種配置選項(xiàng)(成熟產(chǎn)品的標(biāo)志)。然而,示例應(yīng)用程序僅需要我們修改其中的一小部分。讓我們看看示例應(yīng)用程序需要的屬性:

<config-property>
<config-property-name>ConnectionFactory2Name</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jdbc/MedRecGlobalDataSource
</config-property-value>
</config-property>
<config-property>
<config-property-name>ConnectionFactoryName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>jdbc/MedRecGlobalDataSourceXA
</config-property-value>
</config-property>
<config-property>
<config-property-name>TransactionMode</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>managed</config-property-value>
</config-property>
<config-property>
<config-property-name>LicenseKey</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>XXXXXXXXXXXXXXXXXXXX</config-property-value>
</config-property>
<config-property>
<config-property-name>PersistentClasses</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>
com.bea.medrec.domain.Patient,com.bea.medrec.domain.Address,
com.bea.medrec.domain.User,com.bea.medrec.domain.Physician,
com.bea.medrec.domain.Prescription,com.bea.medrec.domain.Record,
com.bea.medrec.domain.Group,com.bea.medrec.domain.VitalSigns
</config-property-value>
</config-property>

  Kodo需要被告知JDBC數(shù)據(jù)源的JNDI位置,以便它能夠與數(shù)據(jù)庫(kù)交互。事實(shí)上,Kodo需要知道兩個(gè)數(shù)據(jù)源:一個(gè)數(shù)據(jù)源用于處理事務(wù)工作,另一個(gè)用于非事務(wù)工作,因?yàn)镵odo有時(shí)訪問(wèn)數(shù)據(jù)庫(kù)執(zhí)行不屬于全局事務(wù)的工作。您可能已經(jīng)猜到了,ConnectionFactoryName和ConnectionFactory2Name屬性正是用于此目的。每個(gè)屬性的值是WebLogic數(shù)據(jù)源的JNDI名稱。請(qǐng)確保ConnectionFactory2Name引用的數(shù)據(jù)源不將其連接加入全局JTA事務(wù)。

   Kodo還需要知道此應(yīng)用程序是否正在使用JTA管理的事務(wù)或本地事務(wù)。因?yàn)榭刂剖聞?wù)的MedRec服務(wù)bean被配置為使用JTA,所以我們將TransactionMode屬性值設(shè)為托管。此外,還需要在資源適配器文件中配置Kodo許可,并列出應(yīng)用程序?qū)⑹褂玫某志没?。用于此用途的屬性?yīng)該是自解釋的。

   如果您曾經(jīng)考察過(guò)JPA規(guī)范,您可能聽(tīng)說(shuō)過(guò)persistence.xml文件。這是包含與JPA持久化相關(guān)的元數(shù)據(jù)的標(biāo)準(zhǔn)配置文件。Kodo還支持persistence.xml文件。然而,在應(yīng)用服務(wù)器環(huán)境中使用Kodo的當(dāng)前早期訪問(wèn)版本時(shí),persistence.xml文件僅包含資源適配器配置中指定的信息的子集,且只能通過(guò)工具(例如Kodo增強(qiáng)程序)使用。

下載

  以下下載包括本文所討論的MedRec版本的所有Java和其他源文件,以及Spring 2.0和Kodo二進(jìn)制文件及其依賴性:medrec-spring-jpa.zip。您大概需要27MB的磁盤空間來(lái)存放下載文件。

結(jié)束語(yǔ)

  本文詳細(xì)考察了對(duì)Spring 2.0中的新增JPA支持的使用,該特性被用于重新實(shí)現(xiàn)MedRec示例應(yīng)用程序的數(shù)據(jù)訪問(wèn)層。希望本文為您提供了開(kāi)始使用這些新API所需的信息。如果您希望進(jìn)行更深入地研究,我們建議您考察本文所包含的實(shí)際MedRec代碼。示例應(yīng)用程序代碼演示了如何以集成方式協(xié)同使用WebLogic 9.1、Spring 2.0和Kodo JPA。

   我們已經(jīng)看到新的JPA API易用、直觀且具有靈活性。Java注釋在降低指定數(shù)據(jù)庫(kù)映射所需的元數(shù)據(jù)數(shù)量方面表現(xiàn)突出。雖然JPA是新增的,但是其強(qiáng)大功能足以允許我們繼續(xù)在持久化層、Web層和Web服務(wù)層中的應(yīng)用程序中使用域模型類。此重用大大簡(jiǎn)化了應(yīng)用架構(gòu),并顯著降低了開(kāi)發(fā)人員需要編寫(xiě)的Java類的數(shù)量。

   而Spring 2.0方面提供了一個(gè)利用JPA創(chuàng)建數(shù)據(jù)訪問(wèn)對(duì)象的優(yōu)秀工具。Spring的數(shù)據(jù)訪問(wèn)架構(gòu)使得我們可以輕松地在不同持久化技術(shù)之間進(jìn)行轉(zhuǎn)換,而無(wú)需重寫(xiě)其他應(yīng)用程序代碼。

參考資料

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多