Hibernate,JPA 對象關係映射之簡單映射策略

簡單映射

近年來 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)。java

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

首先用個小例子介紹一下如何將一個單個 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 類,如今加上這兩個註解以後,就能夠做爲一個實體類與數據庫中的表相對應。他在數據庫中的對應的表爲:app

圖 1. Employee 表對應的 ER 圖

圖 1. Employee 表對應的 ER 圖

映射規則:框架

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

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

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

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

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 圖

圖 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. 基於屬性註解

圖 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 圖

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

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

相關文章
相關標籤/搜索