程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Hibernate及JPA 對象關系映射的簡單映射策略

Hibernate及JPA 對象關系映射的簡單映射策略

編輯:關於JAVA

簡單映射

近年來 ORM(Object-Relational Mapping,對象關系映射,即實體對象和數據庫表的映射)技術市場 熱鬧非凡,各種各樣的持久化框架應運而生,其中影響最大的是 Hibernate 和 Toplink。Sun 公司在充分吸收現有的優秀 ORM 尤其是 Hibernate 框架設計思想的基礎上,制定了新的 JPA(Java Persistence API)規范,對現在亂象叢生的持久 化市場帶來一個標准,大有統一持久化市場的氣勢。JPA 是通過 JDK5.0 注解或 XML 描述對象 - 關系表的映射關系,並將 運行期實體對象持久化到數據庫中去。JPA 規范小組的領導人就是 Hibernate 的發明者 Gavin King,JPA 規范的制定過程 中大量參考了 Hibernate 的內容,所以如果一個對 Hibernate 很熟悉的人,使用起來 JPA 會是輕車熟路,駕輕就熟的, 並且會感覺到更簡單一些,這主要得益於 JDK5 中引入的注解(annotation)。

下面就使用注解介紹一下 JPA 的使 用。

首先用個小例子介紹一下如何將一個單個 Java 類映射到數據庫中。

清單 1. Employee 實體

@Entity
public class Employee implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   private Long id; 
   private String name; 
   private int age; 
   private String addree; 
       
  // Getters and Setters 
}

如果沒有 @javax.persistence.Entity 和 @javax.persistence.Id 這兩個注解的話,它完全就是一個典型的 POJO 的 Java 類,現在加上這兩個注解之後,就可以作為一個實體類與數據庫中的表相對應。他在數據庫中的對應的表為 :

圖 1. Employee 表對應的 ER 圖

映射規則:

1. 實體類必須用 @javax.persistence.Entity 進行注解;

2. 必須使用 @javax.persistence.Id 來注解一個主鍵;

3. 實體 類必須擁有一個 public 或者 protected 的無參構造函數,之外實體類還可以擁有其他的構造函數;

4. 實體類必 須是一個頂級類(top-level class)。一個枚舉(enum)或者一個接口(interface)不能被注解為一個實體;

5. 實體類不能是 final 類型的,也不能有 final 類型的方法;

6. 如果實體類的一個實例需要用傳值的方式調用(例 如,遠程調用),則這個實體類必須實現(implements)java.io.Serializable 接口。

將一個 POJO 的 Java 類映 射成數據庫中的表如此簡單,這主要得益於 Java EE 5種引入的  Configuration by Exception 的理念,這個理念的核 心就是容器或者供應商提供一個缺省的規則,在這個規則下程序是可以正確運行的,如果開發人員有特殊的需求,需要改變 這個默認的規則,那麼就是對默認規則來說就是一個異常(Exception)。

如上例所示:默認的映射規則就是數據庫 表的名字和對應的 Java 類的名字相同,表中列的名字和 Java 類中相對應的字段的名字相同。

現在我們可以改變 這種默認的規則:

清單 2. 使用 @Table 和 @Column 注解修改映射規則

@Entity
@Table(name="Workers") 
public class Employee implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue
   private Long id; 
   @Column(name="emp_name", length=30) 
   private String name; 
   @Column(name="emp_age", nullable=false) 
   private int age; 
   @Column(name="emp_address", nullable=false ,unique=true) 
   private String addree; 
       
    // Getters and Setters 
    
}

改變默認規則後 在數據庫中對應的表為:

圖 2. 修改後的表對應的 ER 圖

首先我們可以可以使用

@ Javax.persistence.Table 這個注解來改變 Java 類在數據庫表種對應的表名。這個注解的定義如下:

清單 3. @Table 注解的定義

@Target(value = {ElementType.TYPE}) 
@Retention(value = RetentionPolicy.RUNTIME) 
public @interface Table { 
    
   public String name() default ""; 
    
   public String catalog() default ""; 
    
   public String schema() default ""; 
    
   public UniqueConstraint[] uniqueConstraints() default {}; 
}

從它的定義上可以看出來,這是一個類級別(class level)的注解 , 只能用在類的前面,其中 name 屬性的值 就是映射到數據庫中時對應表的名字,缺省是類名。

@javax.persistence.Column 注解,定義了列的屬性,你可以 用這個注解改變數據庫中表的列名(缺省情況下表對應的列名和類的字段名同名);指定列的長度;或者指定某列是否可以 為空,或者是否唯一,或者能否更新或插入。

它的定義如下:

清單 4. @Column 注解的定義

@Target(value = {ElementType.METHOD, ElementType.FIELD}) 
@Retention(value = RetentionPolicy.RUNTIME) 
public @interface Column { 
    
   public String name() default ""; 
    
   public boolean unique() default false; 
    
   public boolean nullable() default true; 
    
   public boolean insertable() default true; 
    
   public boolean updatable() default true; 
    
   public String columnDefinition() default ""; 
    
   public String table() default ""; 
    
   public int length() default 255; 
    
   public int precision() default 0; 
    
   public int scale() default 0; 
}

從它的定義可以看出他只可以用在類中的方法前面或者字段前面。

其中 name 屬性的值為數據庫中的列 名,unique 屬性說明該烈是否唯一,nullable 屬性說明是否可以為空,length 屬性指明了該列的最大長度等等。其中 table 屬性將在 @SecondaryTable 的使用中已有過介紹。

JPA 中兩種注解方式

JPA 中將一個類注解成實體 類(entity class)有兩種不同的注解方式:基於屬性(property-based)和基於字段(field-based)的注解。

1 ,基於字段的注解,就是直接將注解放置在實體類的字段的前面。前面的 Employee 實體類就是使用的這種注解方式;

2,基於屬性的注解,就是直接將注解放置在實體類相應的 getter 方法前面,而不是 setter 方法前面(這一點和 Spring 正好相反)。前面的 Employee 實體類如果使用基於屬性注解的方式就可以寫成如下形式。

清單 5. 基於屬 性的注解

@Entity
@Table(name="Employees") 
public class Employee implements Serializable { 
   private static final long serialVersionUID = 1L; 
      
   private Long id; 
   private String name; 
   private int age; 
   private String addree; 
       
   @Id
   @GeneratedValue
   public Long getId() { 
       return id; 
   } 
    
   public void setId(Long id) { 
       this.id = id; 
   } 
  @Column(name="emp_address", nullable=false ,unique=true) 
   public String getAddree() { 
       return addree; 
   } 
    
   public void setAddree(String addree) { 
       this.addree = addree; 
   } 
   @Column(name="emp_age", nullable=false) 
   public int getAge() { 
       return age; 
   } 
    
   public void setAge(int age) { 
       this.age = age; 
   } 
   @Column(name="emp_name", length=30) 
   public String getName() { 
       return name; 
   } 
    
   public void setName(String name) { 
       this.name = name; 
   } 
}

他在數據庫對應的表結構為:

圖 3. 基於屬性注解

可以看出,使用兩種注解方式在數據庫中映 射成的表都是相同的。

但是同一個實體類中必須並且只能使用其中一種注解方式,要麼是基於屬性的注解,要麼是 基於字段的注解。兩種不同的注解方式,在數據庫中對應的數據庫表是相同的,沒有任何區別,開發人員可以根據自己的喜 好任意選用其中一種注解方式。

@SecondaryTable 的使用

上面介紹的幾個例子都是一個實體類映射到數據庫 中的一個表中,那麼能否將一個實體類映射到數據庫兩張或更多表中呢表中呢。在有些情況下如數據庫中已經存在原始數據 類型,並且要求不能更改,這個時候如果能實現一個實體類對應兩張或多張表的話,將是很方便的。JPA2.0 中提供了一個 @SecondaryTablez 注解(annotation)就可以實現這種情況。下面用一個例子說明一下這個注解的使用方法:

清單 6. @SecondaryTable 的使用

@Entity
@SecondaryTables({ 
   @SecondaryTable(name = "Address"), 
   @SecondaryTable(name = "Comments") 
}) 
public class Forum implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue
   private Long id; 
   private String username; 
   private String password; 
   @Column(table = "Address", length = 100) 
   private String street; 
   @Column(table = "Address", nullable = false) 
   private String city; 
   @Column(table = "Address") 
   private String conutry; 
   @Column(table = "Comments") 
   private String title; 
   @Column(table = "Comments") 
   private String Comments; 
   @Column(table = "Comments") 
   private Integer comments_length; 
       
   // Getters and Setters 
}

清單 5 中定義了兩個 Secondary 表,分別為 Address 和 Comments,同時在 Forum 實體類中也通過 @Column 注解將某些子段分別分配給了這兩張表,那些 table 屬性得值是 Adress 的就會存在於 Address 表中,同理 table 屬性 的值是 Comments 的就會存在於 Comments 表中。那些沒有用 @Column 注解改變屬性默認的字段將會存在於 Forum 表中。 圖 4 就是持久化後在數據庫中對應的表的 ER 圖,從圖中可看出來,這些字段如我們預料的一樣被映射到了不同的表中。

圖 4. @SecondaryTable 持久化後對贏得 ER 圖

嵌套映射

在使用嵌套映射的時候首先要有一個被嵌套的類,清單 5 中 Address 實體類使用 @Embeddable 注解 ,說明這個就是一個可被嵌套的類,與 @EmbeddedId 復合主鍵策略中的主鍵類(primary key class)稍有不同的是,這個 被嵌套類不用重寫 hashCode() 和 equals() 方法,復合主鍵將在後面進行介紹。

清單 7. 被嵌套類

@Embeddable
public class Address implements Serializable  { 
    
   private String street; 
   private String city; 
   private String province; 
   private String country; 
    
   // Getters and Setters 
    
}

清單 6 中 Employee 實體類是嵌套類的擁有者,其中使用了 @Embedded 注解將 Address 類嵌套進來了。

清單 8. 嵌套類的使用者

@Entity
public class Employee implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO) 
   private Long id; 
   private String name; 
   private String email; 
   private String cellPhone; 
   @Embedded
   private Address address; 
    
   // Getters and Setters 
  }

清單 7 是持久化後生成的數據庫表,可以看出被嵌套類的屬性,也被持久化到了數據庫中,默認的表名就是 嵌套類的擁有者的類名。

清單 9. 使用嵌套類生成的表結構

CREATE TABLE `employee` ( 
 `ID` bigint(20) NOT NULL, 
 `EMAIL` varchar(255) default NULL, 
 `NAME` varchar(255) default NULL, 
 `CELLPHONE` varchar(255) default NULL, 
 `STREET` varchar(255) default NULL, 
 `PROVINCE` varchar(255) default NULL, 
 `CITY` varchar(255) default NULL, 
 `COUNTRY` varchar(255) default NULL, 
 PRIMARY KEY  (`ID`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

被嵌套類的注解方式,field 方式或者 property 方式,依賴於嵌套 類的擁有者。上面例子中的 Employee 實體類采用的是 field 注解方式,那麼在持久化的過程中,被嵌套類 Address 也是 按照 field 注解方式就行映射的。

我們也可以通過 @Access 注解改變被嵌套類映射方式,清單 8 通過使用 @Access 注解將 Address 被嵌套類的注解方式設定成了 property 方式。清單 9 Employee 仍然采用 filed 注解方式。這 種情況下,持久化的時候,被嵌套類就會按照自己設定的注解方式映射,而不會再依賴於嵌套類的擁有者的注解方式。但這 並不會映射的結果。

清單 10. 基於 property 方式注解的被嵌套類

@Embeddable
@Access(AccessType.PROPERTY) 
public class Address implements Serializable  { 
    
   private String street; 
   private String city; 
   private String province; 
   private String country; 
    
   @Column(nullable=false) 
   public String getCity() { 
       return city; 
   } 
    
   public void setCity(String city) { 
       this.city = city; 
   } 
  @Column(nullable=false,length=50) 
   public String getCountry() { 
       return country; 
   } 
    
   public void setCountry(String country) { 
       this.country = country; 
   } 
   @Column(nullable=false,length=20) 
   public String getProvince() { 
       return province; 
   } 
    
   public void setProvince(String province) { 
       this.province = province; 
   } 
    
   public String getStreet() { 
       return street; 
   } 
    
   public void setStreet(String street) { 
       this.street = street; 
   } 
}

清單 11. 基於 field 方式注解

@Entity
@Access(AccessType. FIELD) 
public class Employee implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO) 
   private Long id; 
   private String name; 
   private String email; 
   private String cellPhone; 
   @Embedded
   private Address address; 
    
   // Getters and Setters 
  }

事先設定被嵌套類的注解方式,是一種應該大力提倡的做法,因為當同一個類被不同的注解方式的類嵌套時, 可能會出現一些錯誤。

總結

簡單映射是 ORM,也就是對象關系映射中較為簡單的一種,他只是數據庫表與類 之間的一一對應,並未涉及表之間的關系,也就未涉及類與類之間的關系,也可以說是其他如繼承映射,關聯關系映射的基 礎,所以說熟悉並掌握簡單關系映射還是很有必要的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved