城市獵人

          在一網情深的日子里,誰能說得清是苦是甜,只知道確定了就義無反顧
          posts - 1, comments - 7, trackbacks - 0, articles - 89

          EJB3 Persistence API標準化持久性(轉)

          Posted on 2008-07-11 10:26 sailor 閱讀(197) 評論(0)  編輯  收藏 所屬分類: EJB3
                  持久性對于大多數企業應用程序都非常關鍵,因為它們需要訪問關系數據庫(例如Oracle Database 10g)。如果您正在使用Java開發應用程序,您可能需要完成一些常規任務(例如數據庫更新和檢索),這是通過編寫JDBC和SQL來完成的。最近幾年,幾種對象關系(O-R)映射框架(例如Oracle TopLink、JBoss Hibernate和BEA Kodo)開始流行,因為它們簡化了持久性問題,將Java開發人員從編寫JDBC代碼的工作中解放出來,從而使他們能夠將精力集中于業務邏輯。一些Java標準(例如EJB 2.x容器管理持久性(CMP)實體bean)也試圖解決持久性挑戰,但是不那么成功。

            雖然存在多種構建應用程序持久層的選擇,但是還沒有一種面向Java平臺的、在Java EE和Java SE環境下均可使用的持久性標準。好消息是EJB3 Java Persistence API (JPA)(它是EJB 3.0規范JSR-220的一部分)的出現,它標準化了面向Java平臺的持久性API。JSR-220為O-R映射供應商(例如TopLink、Hibernate和Kodo)以及其他領先的應用服務器供應商和JDO供應商所廣泛接受。EJB3規范提供了一種極有吸引力的選擇,用于構建企業Java應用程序的持久層。

            在本文中,我將介紹EJB3 Java Persistence API,我將使用一個簡單的域對象模型作為示例。

            域模型

            在構建企業應用程序時,我們首先會設計希望將其保存在數據庫中的域對象模型;然后,與數據庫設計人員合作,確定數據庫模式。域模型表示了持久性對象或實體。實體可以是人、地方或事物,您存儲關于它們的數據。它包含數據和行為。富域模型具有所有面向對象的行為特征,例如繼承性和多態性。

            我們的簡單域模型(圖1)具有Department與Employee實體之間的雙向一對多關系。FullTime和Contractor實體繼承自Employee實體。

          圖1.示例域對象模型
          圖1.示例域對象模型

            O-R框架和EJB3 JPA基礎知識

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

            一種聲明式地執行O-R映射的方式。這種方法(稱為O-R映射元數據)允許將對象映射到一個或多個數據庫表。通常,大多數O-R框架使用XML存儲O-R映射元數據。

            一個用于操作實體(例如,執行CRUD操作)的API。此API允許持久化、檢索、更新或移除對象。基于API和O-R映射元數據的使用,O-R框架代表開發人員執行數據庫操作。此API將開發人員從編寫JDBC或SQL代碼以持久化域對象的工作中解放出來。

            一種用于檢索對象的查詢語言。這是持久性最重要的方面,因為非法的SQL語句可能會降低數據庫的速度。此方法也對應用程序屏蔽了混亂地遍布應用程序的的專有SQL。查詢語言允許檢索實體或對象,并將開發人員從編寫SQL SELECT語句的工作中解放出來。

            EJB3 Java Persistence API (JPA)提供一種標準O-R映射機制、一個執行CRUD操作的EntityManager API以及一種擴展EJB-QL以檢索實體的方式,從而標準化了面向Java平臺的持久性的使用。我將在后面討論這三個方面。

            啟用元數據注釋

            Java SE 5.0引入了元數據注釋。Java EE的所有組件(包括EJB3 JPA)大量使用元數據注釋以簡化企業Java開發。要了解關于元數據注釋的更多信息,請參閱Kyle Downey所著的Bridging the Gap: J2SE 5.0 Annotations。在EJB3 JPA中,注釋可以用于定義對象、關系、O-R映射和持久性上下文的注入。JPA還提供使用XML描述符來代替的選擇。我將主要介紹元數據注釋的使用,因為它們大大簡化了開發。不過,您可能更傾向于在生產部署環境中使用XML
                  
                  標準化JPA中的O-R映射


            定義持久對象:實體

            實體是輕量級的域對象——您希望將其保存在關系數據庫中的Plain Old Java Object (POJO)。像任何POJO一樣,實體可以是抽象或具體類,它能夠擴展另一個POJO。可以使用javax.persistence.Entity注釋將POJO標記為實體。

            以下代碼將使域模型中的Department對象成為實體:

          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;
          }
          }

            每個實體都有一個主鍵;可以在持久字段或屬性上使用Id注釋將其標記為主鍵。實體通過使用字段或屬性(通過setter和getter方法)來保存其狀態。這取決于在哪里使用O-R映射注釋。以上示例使用基于字段的訪問;我們已經使用了具有deptNo字段的Id注釋。要使用基于屬性的訪問,就要使用屬性標記注釋(例如Id),如下所示:

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

            請記住,對一個實體層次結構中的所有實體,必須應用相同的訪問類型(字段或屬性)。

            默認情況下,定義在實體中的每個字段天然就是持久的;如果不希望保存字段/屬性的狀態,則必須將字段/屬性定義為瞬態的,方法是使用@Transient注釋或transient修飾符標記它。

            可嵌入對象

            可嵌入對象是不具有自己標識的持久對象;它是另一個實體的一部分。例如,我們可以假定Address沒有自己的標識,且作為Employee實體的一部分存儲。因此,Address是可嵌入對象的候選。

            可以如下所示創建可嵌入對象:

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

            以下是將對象定義為目標實體中的可嵌入對象的方法:

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

          描述符,因為可以使用它們重寫注釋。

                      關系

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

            既然我們在實體中使用了基于字段的訪問,我們就在Department實體的關系字段上指定注釋,如下所示:

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

            對于雙向關系,必須在關系的另一方指定mappedBy元素(如上),方法是指向擁有此關系的字段或屬性的名稱。

            標準化O-R映射

            可以使用Java元數據注釋或XML實現實體的O-R映射。EJB3 JPA定義了多種用于O-R映射的注釋,例如Table、SecondaryTable、Column、JoinColumn和PrimaryKeyJoinColumn。請參閱EJB3 JPA規范,以獲得關于所有注釋的信息。

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

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

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

            可以使用Column注釋將持久字段或屬性映射到數據庫列,如下所示:

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

            這里,DNAME是持久字段名稱所映射到的列的名稱。如果未使用Column注釋定義O-R映射,則持久性引擎會嘗試將其狀態保存在列中(使用與字段或屬性相同的名稱)。

            實體繼承性

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

            Single-table-per-entity (SINGLE_TABLE)層次結構策略允許將層次結構中的所有實體映射到一個表。在我們的示例中,FullTime和Contractor擴展了Employee,所有這些都可以映射到一個名為EMP的表。換句話說,所有與Employee、FullTime和Contractor有關的數據都被存儲于相同的表內。

            如果使用Joined Subclass策略,則可以將公共持久數據存儲在超類所映射到的表中(例如Employee),并且可以為層次結構中每個子類創建表,以便存儲特定于子類的持久字段。

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

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

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

          @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:用于實體操作的標準API

            javax.persistence.EntityManager管理實體生命周期,并公開了多個在實體上執行CRUD操作的方法。

            EntityManager API在事務上下文中調用。可以在EJB容器外部(例如,從一個Web應用程序)調用它,而無需會話bean外觀。

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

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

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

            如果使用應用程序管理的實體管理器,則必須管理其生命周期。可以創建一個應用程序管理實體管理器實例,如下所示:

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

            然后可以使用EntityManager實例在實體上執行CRUD操作。要關閉應用程序管理實體管理器實例,請在完成工作后調用em.close()方法。

            如前所述,必須在事務上下文中執行涉及任何數據庫更改的實體管理器操作。

            下表列出了EntityManager接口的一些用于執行實體操作的關鍵方法。

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

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

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

            在持久化實體時,如果此關系的CascadeType被設置為PERSIST或ALL,則任何對關聯實體的狀態更改也將被持久化。除非正在使用擴展的持久上下文,否則實體將在事務終止后分離。合并操作允許將分離的實體實例與持久上下文合并;分離實體的狀態將與數據庫同步。這將有助于擺脫EJB 2.x中常見的數據傳輸對象(Data Transfer Object,DTO)反模式,因為作為POJO的實體可以在層與層之間傳輸。惟一的要求是實體類必須實現java.io.Serializable接口。 

                  查詢API

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

            JPQL較之EJBQL 2.x的改進

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

            ·簡化了的查詢語法

            ·JOIN操作

            ·Group By和Having Clause

            ·子查詢

            ·動態查詢

            ·指定參數(named parameter)

            ·批量更新和刪除

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

            動態查詢與指定查詢

            可以使用動態查詢或指定查詢(named query)。指定查詢隨實體存儲,可從應用程序重用。

            要創建動態查詢,請使用實體管理器接口的createQuery方法,如下所示:

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

            如果希望將此查詢用作指定查詢,請在實體中使用NamedQuery注釋,如下所示:

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

            要執行指定查詢,首先使用EntityManager接口上的createNamedQuery方法創建一個Query實例,如下所示:

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

            指定參數

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

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

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

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

            打包

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

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

            此描述符標識持久性提供程序、持久單元和持久單元所使用的數據源。正如其名稱所暗示的,持久單元是集中管理的實體的集合。如果有一個定義在特定模塊中的持久單元,就不需要在persistence.xml中標識實體類;它將由持久性提供程序動態發現。

            參考實現

            BEA Kodo以及Oracle TopLink的TopLink Essentials都提供了EJB3 JPA的參考實現。它可分別從Open JPA和GlassFish開源項目中得到。

            您可以在參考實現服務器或其他任何服從EJB3 JPA的應用服務器上使用本文中的代碼。

            EJB3 JPA工具

            開發工具確實能夠幫助構建更好的應用程序,而如果使用XML實現O-R映射,情況可能就不太妙了。Eclipse Dali O-R映射項目,Oracle JDeveloper 10.1.3和BEA Workshop studio之類的工具都支持EJB3 JPA。

            結束語

            EJB3 Java Persistence API標準化了面向Java平臺的持久性API。它通過使用元數據注釋和異常配置方法,簡化了透明持久性的使用。多種應用服務器已支持EJB3規范(編者注:BEA已經發布WebLogic Server EJB 3.0 Tech Preview )。隨著Java EE 5.0和EJB 3.0規范的完成,您將很快看到許多一流的應用服務器和持久性提供程序會實現EJB3 Java Persistence API。您可以使用來自GlassFish項目的 參考實現 來啟用EJB3 Persistence。

                  原文:http://dev.yesky.com/396/2498396.shtml
          主站蜘蛛池模板: 沂源县| 涡阳县| 叙永县| 汪清县| 孝义市| 保康县| 治多县| 安远县| 永川市| 邢台市| 濉溪县| 筠连县| 阿瓦提县| 镇平县| 仁怀市| 西畴县| 平塘县| 利川市| 樟树市| 凌海市| 高碑店市| 阜平县| 兰坪| 正安县| 信阳市| 清流县| 抚州市| 巩留县| 赫章县| 岳阳县| 阿拉善右旗| 上虞市| 昌江| 通城县| 嘉义市| 晋江市| 柳州市| 民丰县| 牙克石市| 全南县| 会同县|