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

JPA中的主鍵生成策略

編輯:關於JAVA

Table 策略 (Table strategy)

這種策略中,持久化引擎 (persistence engine) 使用關系型數據庫中的一個表 (Table) 來生成主鍵。這種策略可移植性比較好,因為所有的關系型數據庫都支持這種策略。不同的 J2EE 應用服務器使用 不同的持久化引擎。

下面用一個例子來說明這種表生成策略的使用:

清單 1.Table 生成策略

@Entity
 public class PrimaryKey_Table { 
    
 @TableGenerator(name = "PK_SEQ", 
 table = "SEQUENCE_TABLE", 
                pkColumnName  = "SEQUENCE_NAME", 
                valueColumnName  = "SEQUENCE_COUNT") 
    
 @Id
    @GeneratedValue(strategy =GenerationType.TABLE,generator="PK_SEQ") 
    private Long id; 
    
 //Getters and Setters 
//為了方便,類裡面除了一個必需的主鍵列,沒有任何其他列,以後類似
                
 }

首先,清單 1 中使用 @javax.persistence.TableGenerator 這個注解來指定一個用來生成主鍵的表 (Table) ,這個注解可以使用在實體類上,也可以像這個例子一樣使用在主鍵字段上。

其中,在這個例子中,name 屬性 “PK_SEQ” 標示了這個生成器,也就是說這個生成器的名字是 PK_SEQ。這個 Table 屬性標示了用哪個表來存貯生成的主 鍵,在這個例子中,用“ SEQUENCE_TABLE” 來存儲主鍵,數據庫中有對應的 SEQUENCE_TABLE 表。其中 pkColumnName 屬 性用來指定的是生成器那個表中的主鍵,也就是 SEQUENCE_TABLE 這個表的主鍵的名字。屬性 valueColumnName 指定列是 用來存儲最後生成的那個主鍵的值。

也可以使用持久化引擎提供的缺省得 Table,例如:

清單 2. 使用確省 的表生成器

public class PK implements Serializable { 
   private static final long serialVersionUID = 1L; 
     
   @Id
   @GeneratedValue(strategy = GenerationType.TABLE) 
      
   private Long id; 
// Getters and Setters 
}

不同的持久化引擎有不同的缺省值,在 glass fish 中,Table 屬性的缺省值是 SEQUENCE, pkColumnName 屬性 缺省值是 SEQ_NAME,,valueColumnName 屬性的缺省值是 SEQ_COUNT

Sequence 策略

一些數據庫,比如 Oralce,有一種內置的叫做“序列” (sequence)的機制來生成主鍵。為了調用這個序列,需要使用 @javax.persistence.SequenceGenerator 這個注解。

例如

清單 3.sequence 策略生成主鍵

public 

class PK_Sequence implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @SequenceGenerator(name="PK_SEQ_TBL",sequenceName="PK_SEQ_NAME") 
   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE,generator="PK_SEQ_TBL") 
   private Long id; 
// Getters and Setters 
}

其中的 @javax.persistence.SequenceGenerator 定義如下:

清單 4.@SequenceGenerator 注解的定義

@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) 
@Retention(value = RetentionPolicy.RUNTIME) 
public @interface SequenceGenerator { 
    
   public String name(); 
    
   public String sequenceName() default ""; 
    
   public String catalog() default ""; 
    
   public String schema() default ""; 
    
   public int initialValue() default 1; 
    
   public int allocationSize() default 50; 
}

從定義中可以看出這個注解可以用在類上,也可以用在方法和字段上,其中 name 屬性指定的是所使用的生成器 ;sequenceName 指定的是數據庫中的序列;initialValue 指定的是序列的初始值,和 @TableGenerator 不同是它的缺省 值 1;allocationSize 指定的是持久化引擎 (persistence engine) 從序列 (sequence) 中讀取值時的緩存大小,它的缺 省值是 50。

Identity 策略

一些數據庫,用一個 Identity 列來生成主鍵,使用這個策略生成主鍵的時候, 只需要在 @GeneratedValue 中用 strategy 屬性指定即可。如下所示:

清單 5.strategy 策略生成主鍵

@Entity
public class PK_Identity implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY) 
   private Long id; 
// Getters and Setters 
}

Auto 策略

使用 AUTO 策略就是將主鍵生成的策略交給持久化引擎 (persistence engine) 來決定,由它 自己選擇從 Table 策略,Sequence 策略和 Identity 策略三種策略中選擇合適的主鍵生成策略。不同的持久化引擎 (persistence engine) 使用不同的策略,在 galss fish 中使用的是 Table 策略。

使用 AUTO 策略時,我們可以 顯示使用,如:

清單 6.Auto 策略生成主鍵

@Entity
public class PK_Auto implements Serializable { 
   private static final long serialVersionUID = 1L; 
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO) 
   private Long id; 
    
  // Getters and Setters 
   }

或則只使用:

@Generated Value

或者干脆什麼都不寫,因為缺省得主鍵生成策略就是 AUTO。

復合主鍵

在對象關系映射模型中,使用單獨的一個字段作為主鍵是一種非常好的做法,但是在實際應用中, 經常會遇到復合主鍵的問題,就是使用兩個或兩個以上的字段作為主鍵。比如,在一些歷史遺留的數據庫表中,經常出現復 合主鍵的問題,為了解決這種問題,JPA2.0 中采用的 @EmbeddedId 和 @IdClass 兩種方法解決這種問題。它們都需要將用 於主鍵的字段單獨放在一個主鍵類 (primary key class) 裡面,並且該主鍵類必須重寫 equals () 和 hashCode () 方法 ,必須實現 Serializable 接口,必須擁有無參構造函數。

@EmbeddedId 復合主鍵

清單 7 中的 NewsId 類 被用做主鍵類,它用 @Embeddable 注解進行了注釋,說明這個類可以嵌入到其他類中。之外這個類中還重寫了 hashCode () 和 equals () 方法, 因為這個類中的兩個屬性要用作主鍵,必須有一種判定它們是否相等並且唯一的途徑。

清 單 7.@EmbeddedId 中的主鍵類

@Embeddable
 public class NewsId implements Serializable { 
    private static final long serialVersionUID = 1L; 
    
    private String title; 
    private String language; 
    
    public String getLanguage() { 
        return language; 
    } 
    
    public void setLanguage(String language) { 
        this.language = language; 
    } 
    
    public String getTitle() { 
        return title; 
    } 
    
    public void setTitle(String title) { 
        this.title = title; 
    } 
    
    @Override
    public boolean equals(Object obj) { 
        if (obj == null) { 
            return false; 
        } 
        if (getClass() != obj.getClass()) { 
            return false; 
        } 
        final NewsId other = (NewsId) obj; 
if ((this.title == null) ? (other.title != null) : !this.title.equals(other.title)) { 
            return false; 
        } 
if ((this.language == null) ? (other.language != null) : !this.language.equals(
    other.language)) { 
            return false; 
        } 
        return true; 
    } 
    
    @Override
    public int hashCode() { 
        int hash = 5; 
        hash = 41 * hash + (this.title != null ? this.title.hashCode() : 0); 
        hash = 41 * hash + (this.language != null ? this.language.hashCode() : 0); 
        return hash; 
    } 
      
    
 }

清單 8 中的 News 類使用了清單 7 中定義的主鍵類,可以看倒非常簡單,只需要使用 @EmbeddedId 指定主鍵 類即可。

清單 8.News 實體類使用定義好的主鍵類

@Entity
public class News implements Serializable { 
   private static final long serialVersionUID = 1L; 
    
   @EmbeddedId
   private NewsId id; 
   private String content; 
    
  // Getters and Setters 
    
}

清單 9 是持久化後生成的數據庫表的結構,可以看出來這個表如我們預想的一樣是 Title 和 Language 的聯合 主鍵。

清單 9. 使用主鍵類生成的表結構

CREATE TABLE `news` ( 
 `CONTENT` varchar(255) default NULL, 
 `TITLE` varchar(255) NOT NULL, 
 `LANGUAGE` varchar(255) NOT NULL, 
 PRIMARY KEY  (`TITLE`,`LANGUAGE`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

IdClass 復合主鍵

IdClass 這種復合主鍵策略,在主鍵類上 和 Embeddable 這種復合主鍵策略稍有不同。如清單 10,這個策略中的主鍵類不需要使用任何注解 (annotation),但是仍 然必須重寫 hashCode() 和 equals() 兩個方法。其實也就是將 Embeddable 這種復合主鍵策略中的主鍵類的 @Embeddable 注解去掉就可以了。

清單 10. IdClass 復合主鍵策略中的主鍵類

public class NewsId implements 

Serializable { 
    private static final long serialVersionUID = 1L; 
    
    private String title; 
    private String language; 
    
    public String getLanguage() { 
        return language; 
    } 
    
    public void setLanguage(String language) { 
        this.language = language; 
    } 
    
    public String getTitle() { 
        return title; 
    } 
    
    public void setTitle(String title) { 
        this.title = title; 
    } 
    
    @Override
    public boolean equals(Object obj) { 
        if (obj == null) { 
            return false; 
        } 
        if (getClass() != obj.getClass()) { 
            return false; 
        } 
        final NewsId other = (NewsId) obj; 
if ((this.title == null) ? (other.title != null) : !this.title.equals(other.title)) { 
            return false; 
        } 
if ((this.language == null) ? (other.language != null) : !this.language.equals(
    other.language)) { 
            return false; 
        } 
        return true; 
    } 
    
    @Override
    public int hashCode() { 
        int hash = 5; 
        hash = 41 * hash + (this.title != null ? this.title.hashCode() : 0); 
        hash = 41 * hash + (this.language != null ? this.language.hashCode() : 0); 
        return hash; 
    } 
      
    
 }

從清單 11 中可以看出這個 News 實體類首先使用 @IdClass (NewsId.class) 注解指定了主鍵類。同時在類中 也復寫了主鍵類中的兩個字段,並分別用 @Id 進行了注解。

清單 11. IdClass 策略中使用復合主鍵的 News 類

@Entity
@IdClass(NewsId.class) 
public class News implements Serializable { 
   private static final long serialVersionUID = 1L; 
    
   @Id
   private String title; 
   @Id
   private String language; 
private String content; 
// Getters and Setters 
}

從清單 12 中可以看出,兩種復合主鍵的映射策略,持久化後映射到數據庫中的表的結構是相同的,一個明顯的 區別就是在查詢的時候稍有不同。如在使用 @EmbeddableId 策略的時候,要使用如下查詢語句:

Select n.newsId.title from news n

而使用 @IdClass 策略的時候,要使用如下查詢語句:

Select n.title from news n

另外一點就是使用 @IdClass 這種策略的時候,在復寫主鍵類中的字段的時候務必要保證和主鍵類中 的定義完全一樣。

清單 12. @IdClass 策略生成的表結構

CREATE TABLE `news` ( 
 `CONTENT` varchar(255) default NULL, 
 `TITLE` varchar(255) NOT NULL, 
 `LANGUAGE` varchar(255) NOT NULL, 
 PRIMARY KEY  (`TITLE`,`LANGUAGE`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

總結

Java EE 項目開發中的持久層,雖然具體的實現方式, 也就是持久化引擎會隨著你選擇的 Java EE 服務器的不同而有所不同,但是在 JPA(java persistence API) 這個規范之下 ,每個實體的主鍵生成策略卻只有上面幾種,也就是說我們主要掌握了上面幾種主鍵生成策略,就可以在以後 Java EE 項 目持久層開發中以不變應萬變的姿態來面對紛繁復雜的具體情況了。

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