城市獵人

          在一網(wǎng)情深的日子里,誰(shuí)能說(shuō)得清是苦是甜,只知道確定了就義無(wú)反顧
          posts - 1, comments - 7, trackbacks - 0, articles - 89
                  持久性對(duì)于大多數(shù)企業(yè)應(yīng)用程序都非常關(guān)鍵,因?yàn)樗鼈冃枰L問(wèn)關(guān)系數(shù)據(jù)庫(kù)(例如Oracle Database 10g)。如果您正在使用Java開(kāi)發(fā)應(yīng)用程序,您可能需要完成一些常規(guī)任務(wù)(例如數(shù)據(jù)庫(kù)更新和檢索),這是通過(guò)編寫(xiě)JDBC和SQL來(lái)完成的。最近幾年,幾種對(duì)象關(guān)系(O-R)映射框架(例如Oracle TopLink、JBoss Hibernate和BEA Kodo)開(kāi)始流行,因?yàn)樗鼈兒?jiǎn)化了持久性問(wèn)題,將Java開(kāi)發(fā)人員從編寫(xiě)JDBC代碼的工作中解放出來(lái),從而使他們能夠?qū)⒕杏跇I(yè)務(wù)邏輯。一些Java標(biāo)準(zhǔn)(例如EJB 2.x容器管理持久性(CMP)實(shí)體bean)也試圖解決持久性挑戰(zhàn),但是不那么成功。

            雖然存在多種構(gòu)建應(yīng)用程序持久層的選擇,但是還沒(méi)有一種面向Java平臺(tái)的、在Java EE和Java SE環(huán)境下均可使用的持久性標(biāo)準(zhǔn)。好消息是EJB3 Java Persistence API (JPA)(它是EJB 3.0規(guī)范JSR-220的一部分)的出現(xiàn),它標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。JSR-220為O-R映射供應(yīng)商(例如TopLink、Hibernate和Kodo)以及其他領(lǐng)先的應(yīng)用服務(wù)器供應(yīng)商和JDO供應(yīng)商所廣泛接受。EJB3規(guī)范提供了一種極有吸引力的選擇,用于構(gòu)建企業(yè)Java應(yīng)用程序的持久層。

            在本文中,我將介紹EJB3 Java Persistence API,我將使用一個(gè)簡(jiǎn)單的域?qū)ο竽P妥鳛槭纠?

            域模型

            在構(gòu)建企業(yè)應(yīng)用程序時(shí),我們首先會(huì)設(shè)計(jì)希望將其保存在數(shù)據(jù)庫(kù)中的域?qū)ο竽P停蝗缓螅c數(shù)據(jù)庫(kù)設(shè)計(jì)人員合作,確定數(shù)據(jù)庫(kù)模式。域模型表示了持久性對(duì)象或?qū)嶓w。實(shí)體可以是人、地方或事物,您存儲(chǔ)關(guān)于它們的數(shù)據(jù)。它包含數(shù)據(jù)和行為。富域模型具有所有面向?qū)ο蟮男袨樘卣鳎缋^承性和多態(tài)性。

            我們的簡(jiǎn)單域模型(圖1)具有Department與Employee實(shí)體之間的雙向一對(duì)多關(guān)系。FullTime和Contractor實(shí)體繼承自Employee實(shí)體。

          圖1.示例域?qū)ο竽P?
          圖1.示例域?qū)ο竽P?

            O-R框架和EJB3 JPA基礎(chǔ)知識(shí)

            如果使用過(guò)O-R映射框架(例如Oracle TopLink)構(gòu)建應(yīng)用程序持久層,您就會(huì)注意到,每種框架都提供三種工具:

            一種聲明式地執(zhí)行O-R映射的方式。這種方法(稱為O-R映射元數(shù)據(jù))允許將對(duì)象映射到一個(gè)或多個(gè)數(shù)據(jù)庫(kù)表。通常,大多數(shù)O-R框架使用XML存儲(chǔ)O-R映射元數(shù)據(jù)。

            一個(gè)用于操作實(shí)體(例如,執(zhí)行CRUD操作)的API。此API允許持久化、檢索、更新或移除對(duì)象。基于API和O-R映射元數(shù)據(jù)的使用,O-R框架代表開(kāi)發(fā)人員執(zhí)行數(shù)據(jù)庫(kù)操作。此API將開(kāi)發(fā)人員從編寫(xiě)JDBC或SQL代碼以持久化域?qū)ο蟮墓ぷ髦薪夥懦鰜?lái)。

            一種用于檢索對(duì)象的查詢語(yǔ)言。這是持久性最重要的方面,因?yàn)榉欠ǖ腟QL語(yǔ)句可能會(huì)降低數(shù)據(jù)庫(kù)的速度。此方法也對(duì)應(yīng)用程序屏蔽了混亂地遍布應(yīng)用程序的的專有SQL。查詢語(yǔ)言允許檢索實(shí)體或?qū)ο螅㈤_(kāi)發(fā)人員從編寫(xiě)SQL SELECT語(yǔ)句的工作中解放出來(lái)。

            EJB3 Java Persistence API (JPA)提供一種標(biāo)準(zhǔn)O-R映射機(jī)制、一個(gè)執(zhí)行CRUD操作的EntityManager API以及一種擴(kuò)展EJB-QL以檢索實(shí)體的方式,從而標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性的使用。我將在后面討論這三個(gè)方面。

            啟用元數(shù)據(jù)注釋

            Java SE 5.0引入了元數(shù)據(jù)注釋。Java EE的所有組件(包括EJB3 JPA)大量使用元數(shù)據(jù)注釋以簡(jiǎn)化企業(yè)Java開(kāi)發(fā)。要了解關(guān)于元數(shù)據(jù)注釋的更多信息,請(qǐng)參閱Kyle Downey所著的Bridging the Gap: J2SE 5.0 Annotations。在EJB3 JPA中,注釋可以用于定義對(duì)象、關(guān)系、O-R映射和持久性上下文的注入。JPA還提供使用XML描述符來(lái)代替的選擇。我將主要介紹元數(shù)據(jù)注釋的使用,因?yàn)樗鼈兇蟠蠛?jiǎn)化了開(kāi)發(fā)。不過(guò),您可能更傾向于在生產(chǎn)部署環(huán)境中使用XML
                  
                  標(biāo)準(zhǔn)化JPA中的O-R映射


            定義持久對(duì)象:實(shí)體

            實(shí)體是輕量級(jí)的域?qū)ο蟆M麑⑵浔4嬖陉P(guān)系數(shù)據(jù)庫(kù)中的Plain Old Java Object (POJO)。像任何POJO一樣,實(shí)體可以是抽象或具體類,它能夠擴(kuò)展另一個(gè)POJO。可以使用javax.persistence.Entity注釋將POJO標(biāo)記為實(shí)體。

            以下代碼將使域模型中的Department對(duì)象成為實(shí)體:

          package onjava;
          import java.io.Serializable;
          import java.util.Collection;
          import javax.persistence.*;
          @Entity
          @NamedQuery(name="findAllDepartment", query="select o from Department o")
          @Table(name="DEPT")
          public class Department implements Serializable {
          @Id
          @Column(nullable=false)
          protected Long deptNo;
          @Column(name="DNAME")
          protected String name;
          @Column(name="LOC")
          protected String location;
          @OneToMany(mappedBy="department")
          protected Collection employees;
          public Department() {
          }
          ...
          public Collection getEmployees() {
          return employees;
          }
          public void setEmployees(Collection employees) {
          this.employees = employees;
          }
          public Employee addEmployee(Employee employee) {
          getEmployees().add(employee);
          employee.setDepartment(this);
          return employee;
          }
          public Employee removeEmployee(Employee employee) {
          getEmployees().remove(employee);
          employee.setDepartment(null);
          return employee;
          }
          }

            每個(gè)實(shí)體都有一個(gè)主鍵;可以在持久字段或?qū)傩陨鲜褂肐d注釋將其標(biāo)記為主鍵。實(shí)體通過(guò)使用字段或?qū)傩裕ㄍㄟ^(guò)setter和getter方法)來(lái)保存其狀態(tài)。這取決于在哪里使用O-R映射注釋。以上示例使用基于字段的訪問(wèn);我們已經(jīng)使用了具有deptNo字段的Id注釋。要使用基于屬性的訪問(wèn),就要使用屬性標(biāo)記注釋(例如Id),如下所示:

          @Id
          public Long getDeptNo() {
          return deptNo;
          }
          public void setDeptNo(Long deptNo) {
          this.deptNo = deptNo;
          }

            請(qǐng)記住,對(duì)一個(gè)實(shí)體層次結(jié)構(gòu)中的所有實(shí)體,必須應(yīng)用相同的訪問(wèn)類型(字段或?qū)傩裕?br />
            默認(rèn)情況下,定義在實(shí)體中的每個(gè)字段天然就是持久的;如果不希望保存字段/屬性的狀態(tài),則必須將字段/屬性定義為瞬態(tài)的,方法是使用@Transient注釋或transient修飾符標(biāo)記它。

            可嵌入對(duì)象

            可嵌入對(duì)象是不具有自己標(biāo)識(shí)的持久對(duì)象;它是另一個(gè)實(shí)體的一部分。例如,我們可以假定Address沒(méi)有自己的標(biāo)識(shí),且作為Employee實(shí)體的一部分存儲(chǔ)。因此,Address是可嵌入對(duì)象的候選。

            可以如下所示創(chuàng)建可嵌入對(duì)象:

          @Embeddable
          public class Address {
          protected String streetAddr1;
          protected String streetAddr2;
          protected String city;
          protected String state;
          ..
          }

            以下是將對(duì)象定義為目標(biāo)實(shí)體中的可嵌入對(duì)象的方法:

          @Entity
          public class Employee {
          @Id
          @GeneratedValue(strategy=GenerationType.AUTO)
          protected Long id;
          ...
          @Embedded
          protected Address address;
          ...
          }

          描述符,因?yàn)榭梢允褂盟鼈冎貙?xiě)注釋。

                      關(guān)系

            在一個(gè)典型的域模型中,實(shí)體是彼此關(guān)聯(lián)的,或者它們相互之間存在著關(guān)系。兩個(gè)實(shí)體之間的關(guān)系可以是一對(duì)一、一對(duì)多、多對(duì)一和多對(duì)多的。這些關(guān)系可以分別使用OneToOne、OneToMany、ManyToOne或ManyToMany注釋表示。在我們的示例中,Department和Employee實(shí)體之間具有雙向OneToMany關(guān)系。

            既然我們?cè)趯?shí)體中使用了基于字段的訪問(wèn),我們就在Department實(shí)體的關(guān)系字段上指定注釋,如下所示:

          @OneToMany(mappedBy="department")
          protected Collection<Employee> employees ;

            對(duì)于雙向關(guān)系,必須在關(guān)系的另一方指定mappedBy元素(如上),方法是指向擁有此關(guān)系的字段或?qū)傩缘拿Q。

            標(biāo)準(zhǔn)化O-R映射

            可以使用Java元數(shù)據(jù)注釋或XML實(shí)現(xiàn)實(shí)體的O-R映射。EJB3 JPA定義了多種用于O-R映射的注釋,例如Table、SecondaryTable、Column、JoinColumn和PrimaryKeyJoinColumn。請(qǐng)參閱EJB3 JPA規(guī)范,以獲得關(guān)于所有注釋的信息。

            在我們的示例中,可以使用Table注釋定義實(shí)體被映射到的表,如下所示:

          @Table(name="DEPT")
          public class Department implements Serializable {

            EJB3 JPA嚴(yán)重依賴于默認(rèn)設(shè)置,因此如果未定義表映射,則持久性提供程序會(huì)假定此實(shí)體被映射到與實(shí)體類同名的表(在我們的示例中是DEPARTMENT)。如果實(shí)體被映射到多個(gè)表,則可以使用SecondaryTable注釋。

            可以使用Column注釋將持久字段或?qū)傩杂成涞綌?shù)據(jù)庫(kù)列,如下所示:

          @Column(name="DNAME")
          protected String name;

            這里,DNAME是持久字段名稱所映射到的列的名稱。如果未使用Column注釋定義O-R映射,則持久性引擎會(huì)嘗試將其狀態(tài)保存在列中(使用與字段或?qū)傩韵嗤拿Q)。

            實(shí)體繼承性

            EJB3 JPA支持多種實(shí)體繼承性方法。它需要兩種類型的繼承性表映射策略:Single-table-per-entity層次結(jié)構(gòu)策略和Joined-Subclass策略。最好避免使用可選的table-per-class層次結(jié)構(gòu)。

            Single-table-per-entity (SINGLE_TABLE)層次結(jié)構(gòu)策略允許將層次結(jié)構(gòu)中的所有實(shí)體映射到一個(gè)表。在我們的示例中,F(xiàn)ullTime和Contractor擴(kuò)展了Employee,所有這些都可以映射到一個(gè)名為EMP的表。換句話說(shuō),所有與Employee、FullTime和Contractor有關(guān)的數(shù)據(jù)都被存儲(chǔ)于相同的表內(nèi)。

            如果使用Joined Subclass策略,則可以將公共持久數(shù)據(jù)存儲(chǔ)在超類所映射到的表中(例如Employee),并且可以為層次結(jié)構(gòu)中每個(gè)子類創(chuàng)建表,以便存儲(chǔ)特定于子類的持久字段。

            必須在超類中使用Inheritance注釋,以指定繼承類型,如以下代碼所示。此示例展示了使用single-table-per-entity層次結(jié)構(gòu)策略的實(shí)體層次結(jié)構(gòu)。

          @Entity
          @Table(name="EMP")
          @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
          @DiscriminatorColumn(name="EMPLOYEE_TYPE",
          discriminatorType=DiscriminatorType.STRING, length=1)
          public abstract class Employee implements Serializable {
          ...
          }

            每個(gè)子類必須指定用于該實(shí)體類型的鑒別器值,如下所示:

          @Entity
          @DiscriminatorValue(value="F")
          public class FullTime extends Employee {
          @Column(name="SAL")
          protected Double salary;
          @Column(name="COMM")
          protected Double commission;
          @Column(name="DESIG")
          protected String designation;
          ...
          }

            Entity Manager API:用于實(shí)體操作的標(biāo)準(zhǔn)API

            javax.persistence.EntityManager管理實(shí)體生命周期,并公開(kāi)了多個(gè)在實(shí)體上執(zhí)行CRUD操作的方法。

            EntityManager API在事務(wù)上下文中調(diào)用。可以在EJB容器外部(例如,從一個(gè)Web應(yīng)用程序)調(diào)用它,而無(wú)需會(huì)話bean外觀。

            在執(zhí)行任何實(shí)體操作之前,必須獲取EntityManager實(shí)例。可以使用容器管理或應(yīng)用程序管理的實(shí)體管理器,可以使用JNDI查找或依賴注入來(lái)獲取EntityManager實(shí)例。正如其名稱所暗示的,Java EE容器管理著容器管理實(shí)體管理器的生命周期。它可能主要在企業(yè)Java應(yīng)用程序中使用。

            可以使用PersistenceContext注入獲取容器管理實(shí)體管理器實(shí)例,如下所示:

          @PersistenceContext(unitName="onjava")
          private EntityManager em;

            如果使用應(yīng)用程序管理的實(shí)體管理器,則必須管理其生命周期。可以創(chuàng)建一個(gè)應(yīng)用程序管理實(shí)體管理器實(shí)例,如下所示:

          @PersistenceUnit(unitName="onjava")
          private EntityManagerFactory emf;
          private EntityManager em = emf.createEntityManager();

            然后可以使用EntityManager實(shí)例在實(shí)體上執(zhí)行CRUD操作。要關(guān)閉應(yīng)用程序管理實(shí)體管理器實(shí)例,請(qǐng)?jiān)谕瓿晒ぷ骱笳{(diào)用em.close()方法。

            如前所述,必須在事務(wù)上下文中執(zhí)行涉及任何數(shù)據(jù)庫(kù)更改的實(shí)體管理器操作。

            下表列出了EntityManager接口的一些用于執(zhí)行實(shí)體操作的關(guān)鍵方法。

          方法 用途
          public void persist(Object entity); 持久化實(shí)體實(shí)例。
          public <T> T merge(T entity); 合并分離的實(shí)體實(shí)例。
          public void remove(Object entity); 移除實(shí)體實(shí)例。
          public <T> T find(Class<T> entityClass, Object primaryKey); 通過(guò)主鍵檢索實(shí)體實(shí)例。
          public void flush(); 使實(shí)體狀態(tài)與數(shù)據(jù)庫(kù)同步。

            可以使用persist()方法持久化實(shí)體實(shí)例。例如,如果想持久化Contractor實(shí)例,請(qǐng)使用以下代碼:

          @PersistenceContext(unitName="onjava")
          private EntityManager em;
          ...
          Contractor pte = new Contractor();
          pte.setName("Nistha")
          pte.setHourlyRate(new Double(100.0));
          em.persist(pte);

            在持久化實(shí)體時(shí),如果此關(guān)系的CascadeType被設(shè)置為PERSIST或ALL,則任何對(duì)關(guān)聯(lián)實(shí)體的狀態(tài)更改也將被持久化。除非正在使用擴(kuò)展的持久上下文,否則實(shí)體將在事務(wù)終止后分離。合并操作允許將分離的實(shí)體實(shí)例與持久上下文合并;分離實(shí)體的狀態(tài)將與數(shù)據(jù)庫(kù)同步。這將有助于擺脫EJB 2.x中常見(jiàn)的數(shù)據(jù)傳輸對(duì)象(Data Transfer Object,DTO)反模式,因?yàn)樽鳛镻OJO的實(shí)體可以在層與層之間傳輸。惟一的要求是實(shí)體類必須實(shí)現(xiàn)java.io.Serializable接口。 

                  查詢API

            對(duì)實(shí)體的檢索是持久性的一個(gè)重要方面。使用EJB3 JPA時(shí),使用Java持久化查詢語(yǔ)言(Java Persistence Query Language,JPQL)表示查詢。JPQL是EJBQL的擴(kuò)展,它是作為EJB 2.0規(guī)范的一部分而引入的。然而,EJB3 JPA解決了EJBQL的所有局限性,并添加了許多新特性,從而成為一種功能強(qiáng)大的查詢語(yǔ)言。

            JPQL較之EJBQL 2.x的改進(jìn)

            以下是EJB3 JPA中的JPQL的新特性:

            ·簡(jiǎn)化了的查詢語(yǔ)法

            ·JOIN操作

            ·Group By和Having Clause

            ·子查詢

            ·動(dòng)態(tài)查詢

            ·指定參數(shù)(named parameter)

            ·批量更新和刪除

            此外,如果希望從特定于數(shù)據(jù)庫(kù)的查詢擴(kuò)展中獲益,則必須對(duì)查詢實(shí)體使用原生(native ) SQL。

            動(dòng)態(tài)查詢與指定查詢

            可以使用動(dòng)態(tài)查詢或指定查詢(named query)。指定查詢隨實(shí)體存儲(chǔ),可從應(yīng)用程序重用。

            要?jiǎng)?chuàng)建動(dòng)態(tài)查詢,請(qǐng)使用實(shí)體管理器接口的createQuery方法,如下所示:

          Query query = em.createQuery(
          "select e from Employee e where e.empNo > 1");
          query.setParameter(1,100);
          return query.getResultList();

            如果希望將此查詢用作指定查詢,請(qǐng)?jiān)趯?shí)體中使用NamedQuery注釋,如下所示:

          @Entity
          @NamedQuery(name="findAllEmployee",
          query="select e from Employee e where e.empNo > 1")
          public abstract class Employee implements Serializable {
          }

            要執(zhí)行指定查詢,首先使用EntityManager接口上的createNamedQuery方法創(chuàng)建一個(gè)Query實(shí)例,如下所示:

          query = em.createNamedQuery(" findAllEmployee");
          query.setParameter(1,100);
          return query.getResultList();

            指定參數(shù)

            可以在EJBQL查詢中使用指定參數(shù)(named parameter)代替位置參數(shù)(positional parameter)。例如,可以將以上查詢重寫(xiě)如下:

            "select e from Employee e where e.empNo > :empNo "

            如果在查詢中使用指定參數(shù),則必須設(shè)置此參數(shù)如下:

          query = em.createNamedQuery("findAllEmployee");
          query.setParameter("empNo",100);
          return query.getResultList();

            打包

            EJB3 JPA標(biāo)準(zhǔn)化了POJO持久性。因此,實(shí)體并不局限于EJB模塊;它們能夠打包到Web模塊、ejb-jar模塊、EAR級(jí)中的庫(kù)模塊或標(biāo)準(zhǔn)jar文件中。也可以在Java SE中使用實(shí)體。必須在包含實(shí)體的檔案文件中打包描述符(persistence.xml),如下所示:

          <persistence>
          <persistence-unit name="onjava">
          <provider>oracle.toplink.essentials.PersistenceProvider</provider>
          <jta-data-source>jdbc/OracleDS</jta-data-source>
          ...
          </persistence-unit>
          </persistence>

            此描述符標(biāo)識(shí)持久性提供程序、持久單元和持久單元所使用的數(shù)據(jù)源。正如其名稱所暗示的,持久單元是集中管理的實(shí)體的集合。如果有一個(gè)定義在特定模塊中的持久單元,就不需要在persistence.xml中標(biāo)識(shí)實(shí)體類;它將由持久性提供程序動(dòng)態(tài)發(fā)現(xiàn)。

            參考實(shí)現(xiàn)

            BEA Kodo以及Oracle TopLink的TopLink Essentials都提供了EJB3 JPA的參考實(shí)現(xiàn)。它可分別從Open JPA和GlassFish開(kāi)源項(xiàng)目中得到。

            您可以在參考實(shí)現(xiàn)服務(wù)器或其他任何服從EJB3 JPA的應(yīng)用服務(wù)器上使用本文中的代碼。

            EJB3 JPA工具

            開(kāi)發(fā)工具確實(shí)能夠幫助構(gòu)建更好的應(yīng)用程序,而如果使用XML實(shí)現(xiàn)O-R映射,情況可能就不太妙了。Eclipse Dali O-R映射項(xiàng)目,Oracle JDeveloper 10.1.3和BEA Workshop studio之類的工具都支持EJB3 JPA。

            結(jié)束語(yǔ)

            EJB3 Java Persistence API標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。它通過(guò)使用元數(shù)據(jù)注釋和異常配置方法,簡(jiǎn)化了透明持久性的使用。多種應(yīng)用服務(wù)器已支持EJB3規(guī)范(編者注:BEA已經(jīng)發(fā)布WebLogic Server EJB 3.0 Tech Preview )。隨著Java EE 5.0和EJB 3.0規(guī)范的完成,您將很快看到許多一流的應(yīng)用服務(wù)器和持久性提供程序會(huì)實(shí)現(xiàn)EJB3 Java Persistence API。您可以使用來(lái)自GlassFish項(xiàng)目的 參考實(shí)現(xiàn) 來(lái)啟用EJB3 Persistence。

                  原文:http://dev.yesky.com/396/2498396.shtml
          主站蜘蛛池模板: 新竹市| 昭平县| 思南县| 昌都县| 内丘县| 长寿区| 乐亭县| 黄平县| 冀州市| 尉氏县| 益阳市| 昭苏县| 南通市| 上林县| 六枝特区| 渭源县| 启东市| 永安市| 农安县| 嘉义县| 巍山| 凉山| 墨竹工卡县| 沈阳市| 故城县| 蒲城县| 金乡县| 襄城县| 宁武县| 桂东县| 勃利县| 习水县| 肇东市| 易门县| 德令哈市| 固始县| 天峨县| 资源县| 卓尼县| 宜兴市| 木兰县|