Hibernate之ID主鍵生成策略

 ID主鍵生成策略


一、 Xml方式

<id>標籤必須配置在<class>標籤內第一個位置。由一個字段構成主鍵,如果是複雜主鍵<composite-id>標籤

被映射的類必須定義對應數據庫表主鍵字段。大多數類有一個JavaBeans風格的屬性, 爲每一個實例包含唯一的標識。<id> 元素定義了該屬性到數據庫表主鍵字段的映射。

<id
       name="propertyName"                                          
       type="typename"                                               
       column="column_name"                                          
       unsaved-value="null|any|none|undefined|id_value"              
       access="field|property|ClassName"                            
       node="element-name|@attribute-name|element/@attribute|.">
 
        <generatorclass="generatorClass"/>
</id>

(1) name (可選): 標識屬性的名字(實體類的屬性)。

(2) type (可選): 標識Hibernate類型的名字(省略則使用hibernate默認類型),也可以自己配置其它hbernate類型(integer, long, short, float,double, character, byte, boolean, yes_no, true_false)

(2) length(可選):當type爲varchar時,設置字段長度

(3) column (可選 - 默認爲屬性名): 主鍵字段的名字(省略則取name爲字段名)。

(4) unsaved-value (可選 - 默認爲一個切合實際(sensible)的值): 一個特定的標識屬性值,用來標誌該實例是剛剛創建的,尚未保存。 這可以把這種實例和從以前的session中裝載過(可能又做過修改--譯者注) 但未再次持久化的實例區分開來。

(5) access (可選 - 默認爲property): Hibernate用來訪問屬性值的策略。

如果 name屬性不存在,會認爲這個類沒有標識屬性。

unsaved-value 屬性在Hibernate3中幾乎不再需要。

還有一個另外的<composite-id>定義可以訪問舊式的多主鍵數據。 我們強烈不建議使用這種方式。


<generator>元素(主鍵生成策略)

主鍵生成策略是必須配置

用來爲該持久化類的實例生成唯一的標識。如果這個生成器實例需要某些配置值或者初始化參數, 用<param>元素來傳遞。

<id name="id"type="long" column="cat_id">
       <generator class="org.hibernate.id.TableHiLoGenerator">
               <param name="table">uid_table</param>
               <param name="column">next_hi_value_column</param>
       </generator>
</id>

所有的生成器都實現org.hibernate.id.IdentifierGenerator接口。 這是一個非常簡單的接口;某些應用程序可以選擇提供他們自己特定的實現。當然, Hibernate提供了很多內置的實現。下面是一些內置生成器的快捷名字:

increment

用於爲long, short或者int類型生成 唯一標識。只有在沒有其他進程往同一張表中插入數據時才能使用。 在集羣下不要使用。

identity

對DB2,MySQL, MS SQL Server,Sybase和HypersonicSQL的內置標識字段提供支持。 返回的標識符是long, short 或者int類型的。 (數據庫自增)

sequence

在DB2,PostgreSQL, Oracle, SAPDB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。(數據庫自增)

hilo

使用一個高/低位算法高效的生成long, short 或者 int類型的標識符。給定一個表和字段(默認分別是 hibernate_unique_key和next_hi)作爲高位值的來源。 高/低位算法生成的標識符只在一個特定的數據庫中是唯一的。

seqhilo

使用一個高/低位算法來高效的生成long, short 或者 int類型的標識符,給定一個數據庫序列(sequence)的名字。

uuid

用一個128-bit的UUID算法生成字符串類型的標識符, 這在一個網絡中是唯一的(使用了IP地址)。UUID被編碼爲一個32位16進制數字的字符串,它的生成是由hibernate生成,一般不會重複。

UUID包含:IP地址,JVM的啓動時間(精確到1/4秒),系統時間和一個計數器值(在JVM中唯一)。 在Java代碼中不可能獲得MAC地址或者內存地址,所以這已經是我們在不使用JNI的前提下的能做的最好實現了

guid

在MS SQL Server 和 MySQL 中使用數據庫生成的GUID字符串。

native

根據底層數據庫的能力選擇identity,sequence 或者hilo中的一個。(數據庫自增)

assigned

讓應用程序在save()之前爲對象分配一個標示符。這是 <generator>元素沒有指定時的默認生成策略。(如果是手動分配,則需要設置此配置)

select

通過數據庫觸發器選擇一些唯一主鍵的行並返回主鍵值來分配一個主鍵。

foreign

使用另外一個相關聯的對象的標識符。通常和<one-to-one>聯合起來使用。

二、 annotateon方式

使用@GeneratedValue(strategy=GenerationType)註解可以定義該標識符的生成策略

Strategy有四個值:

①     、AUTO- 可以是identity column類型,或者sequence類型或者table類型,取決於不同的底層數據庫.

相當於native

②     、TABLE- 使用表保存id值

③     、IDENTITY- identity column

④     、SEQUENCE- sequence

注意:auto是默認值,也就是說沒有後的參數則表示爲auto

1、AUTO默認

@Id
    @GeneratedValue
    public int getId() {
        return id;
}


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id;
}

1、  對於mysql,使用auto_increment

2、  對於oracle使用hibernate_sequence(名稱固定)

2、IDENTITY

@Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    public int getId() {
        return id;
}


對DB2,MySQL, MS SQL Server,Sybase和HypersonicSQL的內置標識字段提供支持。 返回的標識符是long, short 或者int類型的。 (數據庫自增)

注意:此生成策略不支持Oracle

 

3、SEQUENCE

  

  @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    public int getId() {
        return id;
    }

在DB2,PostgreSQL,Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。(數據庫自增)

注意:此生成策略不支持MySQL

 

4、爲Oracle指定定義的Sequence

    a)、首先需要在實體類前面申明一個Sequence如下:

        方法:

@SequenceGenerator(name="SEQ_Name",sequenceName="SEQ_DB_Name")

            參數注意:SEQ_Name:表示爲申明的這個Sequence指定一個名稱,以便使用

                    SEQ_DB_Name:表示爲數據庫中的Sequence指定一個名稱。

                    兩個參數的名稱可以一樣。

@Entity
@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")
public class Teacher {
……
}

b)、然後使用@GeneratedValue註解

    方法:

@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="SEQ_Name")

    參數:strategy:固定爲GenerationType.SEQUENCE

         Generator:在實體類前面申明的sequnce的名稱

@Entity
@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")
public class Teacher {
    private int id;
   
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")
    public int getId() {
        return id;
}}

 

5、TABLE - 使用表保存id值

  原理:就是在數據庫中建立一個表,這個表包含兩個字段,一個字段表示名稱,另一個字段表示值。每次在添加數據時,使用第一個字段的名稱,來取值作爲添加數據的ID,然後再給這個值累加一個值再次存入數據庫,以便下次取出使用。

Table主鍵生成策略的定義:

@javax.persistence.TableGenerator(
        name="Teacher_GEN",              //生成策略的名稱
        table="GENERATOR_TABLE",     //在數據庫生成表的名稱
        pkColumnName = "pk_key",     //表中第一個字段的字段名 類型爲varchar,key
        valueColumnName = "pk_value",    //表中第二個字段的字段名 int ,value
        pkColumnValue="teacher",     //這個策略中使用該記錄的第一個字段的值(key值)
        initialValue = 1,                //這個策略中使用該記錄的第二個字段的值(value值)初始化值
        allocationSize=1                  //每次使用數據後累加的數值
)

這樣執行後,會在數據庫建立一個表,語句如下:

 create table GENERATOR_TABLE (pk_key varchar(255),pk_value integer );


結構:


並且表建立好後,就插入了一個記錄,如下:

    

注:這條記錄的pk_value值爲2,是因爲剛剛做例程序時,已經插入一條記錄了。初始化時爲1。

使用TABLE主鍵生成策略:

@Entity
@javax.persistence.TableGenerator(
        name="Teacher_GEN",              //生成策略的名稱
        table="GENERATOR_TABLE",     //在數據庫生成表的名稱
        pkColumnName = "pk_key",     //表中第一個字段的字段名 類型爲varchar,key
        valueColumnName = "pk_value",    //表中第二個字段的字段名 int ,value
        pkColumnValue="teacher",     //這個策略中使用該記錄的第一個字段的值(key值)
        initialValue = 1,                //這個策略中使用該記錄的第二個字段的值(value值)初始化值
        allocationSize=1                 //每次使用數據後累加的數值
)
public class Teacher {
    private int id;
    @Id
    @GeneratedValue(strategy=GenerationType.TABLE,generator="Teacher_GEN")
    public int getId() {
        return id;}}


注意:這樣每次在添加Teacher記錄時,都會先到GENERATOR_TABLE表取pk_key=teacher的記錄後,使用pk_value值作爲記錄的主鍵。然後再給這個pk_value字段累加1,再存入到GENERATOR_TABLE表中,以便下次使用。

    這個表可以給無數的表作爲主鍵表,只是添加一條記錄而以(需要保證table、pkColumnName、valueColumnName三個屬性值一樣就可以了。),這個主鍵生成策略可以跨數據庫平臺。


三、 聯合主鍵

     複合主鍵(聯合主鍵):多個字段構成唯一性。


1、xml方式

a) 實例場景:覈算期間

// 覈算期間

public class FiscalYearPeriod {   
    private int fiscalYear; //覈算年
    private int fiscalPeriod; //覈算月  
    private Date beginDate; //開始日期  
    private Date endDate; //結束日期
    private String periodSts; //狀態
    public int getFiscalYear() {
        return fiscalYear;
    }
    public void setFiscalYear(int fiscalYear) {
        this.fiscalYear = fiscalYear;
    }
    public int getFiscalPeriod(){  return fiscalPeriod;}
    public void setFiscalPeriod(int fiscalPeriod) {
        this.fiscalPeriod =fiscalPeriod;
    }
    public DategetBeginDate() {return beginDate;}
    public void setBeginDate(DatebeginDate) {  this.beginDate = beginDate; }
    public Date getEndDate(){return endDate;}
    public void setEndDate(DateendDate) {  this.endDate = endDate; }
    public StringgetPeriodSts() {  return periodSts;}
    public voidsetPeriodSts(String periodSts) {this.periodSts = periodSts;}
}


複合主鍵的映射,一般情況把主鍵相關的屬性抽取出來單獨放入一個類中。而這個類是有要求的:必需實現序列化接口(java.io.Serializable)(可以保存到磁盤上),爲了確定這個複合主鍵類所對應對象的唯一性就會產生比較,對象比較就需要複寫對象的hashCode()、equals()方法(複寫方法如下圖片),然後在類中引用這個複合主鍵類

 


 

 

b) 複合主鍵類:

複合主鍵必需實現java.io.Serializable接口:

public class FiscalYearPeriodPK implements java.io.Serializable {
  private int fiscalYear;//覈算年 
  private int fiscalPeriod;//覈算月
  public int getFiscalYear() {
      return fiscalYear;
  }
  public void setFiscalYear(int fiscalYear) {
      this.fiscalYear = fiscalYear;
  }
  public int getFiscalPeriod(){
      return fiscalPeriod;
  }
  public void setFiscalPeriod(int fiscalPeriod) {
      this.fiscalPeriod =fiscalPeriod;
  }  
  @Override
  public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime* result + fiscalPeriod;
      result = prime* result + fiscalYear;
      return result;
  }
  @Override
  public boolean equals(Object obj){
      if (this == obj)
          return true;
      if (obj == null)
          return false;
      if (getClass() !=obj.getClass())
          return false;
      FiscalYearPeriodPKother = (FiscalYearPeriodPK) obj;
      if (fiscalPeriod != other.fiscalPeriod)
          return false;
      if (fiscalYear != other.fiscalYear)
          return false;
      return true;
  }}


c) 實體類:(中引用了複合主鍵類)

public class FiscalYearPeriod{
    private FiscalYearPeriodPK fiscalYearPeriodPK;//引用 複合主鍵類  
    private Date beginDate;//開始日期   
    private Date endDate;//結束日期 
    private String periodSts;//狀態 
    public FiscalYearPeriodPK getFiscalYearPeriodPK() {
        return fiscalYearPeriodPK;
    }
    public void setFiscalYearPeriodPK(FiscalYearPeriodPKfiscalYearPeriodPK) {
        this.fiscalYearPeriodPK = fiscalYearPeriodPK;
    }
………………

 

d) FiscalYearPeriod.hbm.xml映射文件

<hibernate-mapping>
    <class name="com.bjsxt.hibernate.FiscalYearPeriod"table="t_fiscal_year_period">
        <composite-id name="fiscalYearPeriodPK">
            <key-property name="fiscalYear"/>
            <key-property name="fiscalPeriod"/>
        </composite-id>
        <property name="beginDate"/>
        <property name="endDate"/>
        <property name="periodSts"/>
    </class>
</hibernate-mapping>

e) 導出數據庫輸出SQL語句:

create table t_fiscalYearPeriod (fiscalYear integer not null, fiscalPeriodinteger not null, beginDate datetime, endDate datetime, periodSts varchar(255),primary key (fiscalYear, fiscalPeriod))//實體映射到數據就是兩個字段構成複合主鍵

f) 數據庫表結構:



g) 複合主鍵關聯映射數據存儲:

 

   session =HibernateUtils.getSession();
    tx =session.beginTransaction();
 
    FiscalYearPeriod fiscalYearPeriod = new FiscalYearPeriod();
           
    //構造複合主鍵
    FiscalYearPeriodPK pk = new FiscalYearPeriodPK();
    pk.setFiscalYear(2009);
    pk.setFiscalPeriod(11);
           
    fiscalYearPeriod.setFiscalYearPeriodPK(pk);//爲對象設置複合主鍵
    fiscalYearPeriod.setEndDate(new Date());
    fiscalYearPeriod.setBeginDate(new Date());
    fiscalYearPeriod.setPeriodSts("Y");
       
    session.save(fiscalYearPeriod);        

 

h) 執行輸出SQL語句:

Hibernate: insert into t_fiscalYearPeriod (beginDate, endDate, periodSts,fiscalYear, fiscalPeriod) values (?, ?, ?, ?, ?)

注:如果再存入相同複合主鍵的記錄,就會出錯。

i) 數據的加載:

    數據加載非常簡單,只是主鍵是一個對象而以,不是一個普通屬性。

 

2、annotation方式

下面是定義組合主鍵的幾種語法:

將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id

將組件的屬性註解爲@EmbeddedId

將類註解爲@IdClass,並將該實體中所有屬於主鍵的屬性都註解爲@Id

a) 將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id

組件類:

@Embeddable
public class TeacherPK implementsjava.io.Serializable{
    private int id;
    private String name;
    public int getId() {return id;  }
    public void setId(int id) {this.id = id;}
    public String getName() {   return name;}
    public void setName(Stringname) {  this.name = name;}
    @Override
    public boolean equals(Object o) {       ……}
    @Override
    public int hashCode() { return this.name.hashCode();    }
}


將組件類的屬性註解爲@Id,實體類中組件的引用

@Entity
public class Teacher {
  private TeacherPK pk;
  private String title;
 
  @Id
  public TeacherPK getPk(){
      return pk;
}}

 

b)  將組件的屬性註解爲@EmbeddedId

注意:只需要在實體類中表示覆合主鍵屬性前註解爲@Entity,表示此主鍵是一個複合主鍵

       注意了,複合主鍵類不需要任何的注意。

@Entity
public class Teacher {
   private TeacherPK pk;
   private String title;  
 
   @EmbeddedId
   public TeacherPK getPk(){
       return pk;
   }}

c)  類註解爲@IdClass,主鍵的屬性都註解爲@Id

需要將複合主鍵類建立好,不需要進行任何註解

在實體類中不需要進行復合主鍵類的引用

需要在實體類前面註解爲@IdClass,並且指定一個value屬性,值爲複合主鍵類的class

需要在實體類中進行復合主鍵成員屬性前面註解爲@Id

如下:

@Entity
@IdClass(TeacherPK.class)
public class Teacher {
  //private TeacherPK pk;//不再需要
  private int id;
  private String name;   
  @Id
  public int getId() {return id;  }
  public void setId(int id) { this.id = id;   }
 
  @Id
  public String getName() {return name;}
  public void setName(Stringname) {this.name = name;
  }}


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