[JavaEE - JPA] 6. ORM的核心註解 - 基礎類型以及嵌套類型

本文繼續介紹JPA ORM的核心註解中和基礎類型映射相關的部分。

基礎類型映射

所謂的基礎類型映射,實際上就是Java中定義的數據類型應該如何被JDBC轉換成數據庫所支持的數據類型。而這些基礎類型,主要包括了以下9種:

  1. 簡單類型:byte,int,short,long,boolean,char,float以及double
  2. 簡單類型對應的包裝類型:Byte,Integer,Short,Long,Boolean,Character,Float以及Double
  3. 字節以及字符數組:byte[],Byte[],char[]以及Character[]
  4. 能夠表示大數值的類型:java.math.BigInteger以及java.math.BigDecimal
  5. 字符串類型:String
  6. Java時間類型:java.util.Date以及java.util.Calendar
  7. JDBC時間類型:java.sql.Date,java.sql.Time以及java.sql.Timestamp
  8. 枚舉類型
  9. 可序列化的對象(Serializable Object)

在Java源代碼中,可以使用@Basic來標明某個屬性是需要被持久化的。但是這個註解一般而言是可選的,出現在實體類型中的屬性默認就是需要被持久化的。而正是因爲@Basic註解只能夠應用在以上列舉的9種類型之上,所以我們將這些類型命名爲基礎類型,同Basic這個詞的意思。

列映射(Column Mapping)

如果說@Basic是一種邏輯映射(Logical Annotation)的話,那麼與之相對的@Column便是物理映射(Physical Annotation)。它能夠規定屬性應該如何被映射成數據庫表中的列。一般最常用的就是其中的name屬性,它能夠規定將數據轉換到數據庫表中後列的名字。但是,這個註解支持的屬性遠不止一個name,還有很多:

這裏寫圖片描述

限於篇幅,就不一一列舉了。在需要的時候查閱一下即可。

懶加載(Lazy Fetching)

一般說起懶加載,說的都是在集合映射的一對多或者多對多關係下的懶加載。但是對於基礎類型,也是支持懶加載的。這一點恐怕讓很多人覺得有點匪夷所思,但是考慮當一個屬性的數據量非常巨大的時候(比如下面即將提到的大對象),懶加載還是有必要的。

@Basic(fetch=FetchType.LAZY)
@Column
private String article;
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

比如上述的article字段的數據量可能特別大,因此使用了懶加載。通過指定@Basic註解上的fetch屬性來實現。但是需要注意的是,懶加載的聲明對於JPA實現只是一個提示(Hint),表明應用程序希望使用懶加載,但是JPA實現是否真的會實現還取決於它自身是如何實現的。而且,特別注意並不是所有的場合都適合使用懶加載,如果對一個普通的字符串屬性使用懶加載,JPA實現在處理它的時候,還需要額外進行一堆操作,比如爲該屬性加上代理,當屬性被訪問的時候由代理髮出加載數據的請求等等。這些操作都需要消耗時間,內存等資源。因此,盲目地使用懶加載只會讓程序的性能變差。

大對象(Large Object,LOB)

對於JDBC而言,處理擁有大數據量的大對象的方式和處理其它普通對象的方式是有所不同的。因此爲了通知JPA實現某個屬性是大對象,就需要使用@Lob註解。結合@Basic的懶加載,可以聲明如下:

@Entity
public class Employee {
    @Id
    private int id;

    @Basic(fetch=FetchType.LAZY)
    @Lob 
    @Column(name="PIC")
    private byte[] picture;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

@Lob註解本身並不包含任何屬性,它的作用正如上面所討論的那樣,只是爲了某種標註來通知JPA實現。此類註解也可以被稱爲標記註解(Marker Annotation)。

枚舉類型(Enumerated Type)

我們都知道,枚舉類型中有兩個比較常用的方法,一個是ordinal(),一個是name()。它們的作用分別是得到枚舉值的聲明順序和獲取枚舉值的聲明名稱。當枚舉類型被映射到數據庫表中的時候,默認使用的是ordinal()方法的返回值。如果需要使用name()方法的返回值作爲映射後的值,可以使用下面的方式聲明:

@Enumerated(EnumType.STRING)
private EmployeeType type;
  • 1
  • 2
  • 1
  • 2

EnumType還有另一個名爲ORDINAL的取值,它是默認值。

那麼,對於枚舉類型的映射,一般使用哪種比較好呢?通常而言,使用ORDINAL會更好一些(也就是使用默認選項)。因爲這樣的存儲效率會更高一些,畢竟存儲整型值比存儲字符串類型的代價會小一些。但是需要注意的是,如果隨着應用的功能越來越多,枚舉類型的候選值也可能會越來越多,這個時候爲了保證以前存儲的值的有效性,需要注意新追加的枚舉值總是需要被聲明爲最後一個,從而不會影響到前面已有枚舉值的ORDINAL。

時間類型(Temporal Type)

談到時間類型,參與到映射的時間類型實際上有兩種:

  1. Java時間類型:java.util.Date以及java.util.Calendar
  2. JDBC時間類型:java.sql.Date,java.sql.Time以及java.sql.Timestamp

對於第二種JDBC的時間類型,不需要任何註解就能完成正確的映射和轉換。
對於第一種Java的時間類型,就需要使用一些註解了。主要是爲了告知JPA實現在映射和轉換時使用哪一種JDBC時間類型(因此,最終還是使用的JDBC時間類型)。通過@Temporal註解來指定:

@Temporal(TemporalType.DATE)
private Calendar dob;

@Temporal(TemporalType.TIMESTAMP)
private Date startTime;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

TemporalType枚舉類型的可選枚舉值如下所示:

這裏寫圖片描述

瞬態屬性(Transient State Attribute)

如果一個屬性它屬於某個實體類型,但是在持久化到數據庫中的時候,又不需要將它也映射到表結構中,就可以將它設置成”瞬態屬性”。

可以通過兩種方式實現:

  1. 使用transient關鍵字
  2. 使用@Transient註解

至於它倆的區別?那要從transient這個關鍵字的其它用法說起了。我們都知道transient是Java語言中的一個關鍵字,它並非爲JPA而設計。該關鍵字最典型意義是當一個對象參與到序列化的過程中時,被transient修飾的字段是不會參與序列化的。知道了這一點,這兩種實現方式的區別也就很明確了:被@Transient註解修飾的屬性還是會正常參與到序列化過程中,但是被transient關鍵字修飾的就不會了。

嵌套類型(Embedded Type)

介紹完了基礎類型,再來看看嵌套類型。嵌套類型聽起來很高大上的樣子,但是實際上你完全可以把它當作幾個屬性(特別是基礎類型)的集合,而這幾個屬性在邏輯上一般有比較緊密的關係。但是在實際的物理存儲中(數據庫中),嵌套類型的字段還是會和其歸屬實體一起被存儲到一張表中。

從上面討論的特點來看,可以將嵌套類型視爲一種獨立性不那麼強的實體類型,它總是需要依賴於另一個實體類型,不能單獨存在。舉個例子,地址這種實體概念就非常適合被定義爲一個嵌套類型,比如家庭地址,收件地址等等,都擁有共通的幾個字段,但是它們通常是屬於另外一個實體類型,比如客戶這一實體,它就能夠擁有家庭地址以及收件地址。

如果從UML(統一建模語言)來審視這個概念的話,通常適用於使用嵌套類型來表達的實體類型一般都體現在它能夠被”組合(Composition)”到另外一個實體中:

這裏寫圖片描述

解釋清除了嵌套類型的存在意義,下面來看看如何聲明和使用它。

聲明嵌套類型Address:

@Embeddable
public class Address {
    private String street;
    private String city;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用嵌套類型Address:

@Entity
public class Employee {
    @Id 
    private int id;
    private String name;
    private long salary;
    @Embedded 
    private Address address;
    // ...
}

@Entity
public class Company {
    @Id 
    private int id;
    private String name;
    @Embedded 
    private Address address;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

注意@Embeddable@Embedded這兩個註解分別用來聲明和使用嵌套類型,不要弄混了。

在上述代碼中,Employee和Company兩個實體類型都使用Address嵌套類型。因此它們兩個實體類型所對應的數據庫表結構中都會存在Address類型定義的幾個屬性。

那麼有沒有辦法修改嵌套類型被映射到表後的屬性名稱呢?
當然也是可以的,通過@AttributeOverrides@AttributeOverride這兩個註解來實現這一需求:

比如,在Employee類型中,嵌套類型Address的city字段所對應的數據庫列名需要被設置成province,street需要被設置成area。可以使用如下的代碼實現:

@Entity
public class Company {
    @Id 
    private int id;
    private String name;
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="street", column=@Column(name="area")),
        @AttributeOverride(name="city", column=@Column(name="province"))
    })
    private Address address;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

原文地址:http://blog.csdn.net/dm_vincent/article/details/52843078

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章