hibernate-主鍵生成策略

一、assigned

主鍵由外部程序負責生成,在 save() 以前必須指定一個。Hibernate不負責維護主鍵生成。與Hibernate和底層數據庫都無關,能夠跨數據庫。在存儲對象前,必需要使用主鍵的setter方法給主鍵賦值,至於這個值怎麼生成,徹底由本身決定,這種方法應該儘可能避免。算法

配置文件的方式數據庫

<id name="id" column="id">
<generator class="assigned" />
</id>

 

 

註解的方式:服務器

    @Id
    @Column(length = 40)
    @GeneratedValue(generator = "paymentableGenerator")
    @GenericGenerator(name = "paymentableGenerator", strategy = "assigned")

 

「ud」是自定義的策略名,人爲起的名字,後面均用「ud」表示。session

特色:能夠跨數據庫,人爲控制主鍵生成,應儘可能避免。併發

二、increment

由Hibernate從數據庫中取出主鍵的最大值(每一個session只取1次),以該值爲基礎,每次增量爲1,在內存中生成主鍵,不依賴於底層的數據庫,所以能夠跨數據庫。dom

<id name="id" column="id">
<generator class="increment" />
</id>

 

Hibernate調用org.hibernate.id.IncrementGenerator類裏面的generate()方法,使用select max(idColumnName) from tableName語句獲取主鍵最大值。該方法被聲明成了synchronized,因此在一個獨立的Java虛擬機內部是沒有問題的,然而,在多個JVM同時併發訪問數據庫select max時就可能取出相同的值,再insert就會發生Dumplicate entry的錯誤。因此只能有一個Hibernate應用進程訪問數據庫,不然就可能產生主鍵衝突,因此不適合多進程併發更新數據庫,適合單一進程訪問數據庫,不能用於羣集環境。ide

官方文檔:只有在沒有其餘進程往同一張表中插入數據時才能使用,在集羣下不要使用。測試

特色:跨數據庫,不適合多進程併發更新數據庫,適合單一進程訪問數據庫,不能用於羣集環境。ui

三、hilo

hilo(高低位方式high low)是hibernate中最經常使用的一種生成方式,須要一張額外的表保存hi的值。保存hi值的表至少有一條記錄(只與第一條記錄有關),不然會出現錯誤。能夠跨數據庫。spa

<id name="id" column="id">
<generator class="hilo">
<param name="table">hibernate_hilo</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
</id>
<param name="table">hibernate_hilo</param> 指定保存hi值的表名
<param name="column">next_hi</param> 指定保存hi值的列名
<param name="max_lo">100</param> 指定低位的最大值

 

也能夠省略table和column配置,其默認的表爲hibernate_unique_key,列爲next_hi

<id name="id" column="id">
<generator class="hilo">
<param name="max_lo">100</param>
</generator>
</id>

 

hilo生成器生成主鍵的過程(以hibernate_unique_key表,next_hi列爲例):

1. 得到hi值:讀取並記錄數據庫的hibernate_unique_key表中next_hi字段的值,數據庫中此字段值加1保存。

2. 得到lo值:從0到max_lo循環取值,差值爲1,當值爲max_lo值時,從新獲取hi值,而後lo值繼續從0到max_lo循環。

3. 根據公式 hi * (max_lo + 1) + lo計算生成主鍵值。

注意:當hi值是0的時候,那麼第一個值不是0*(max_lo+1)+0=0,而是lo跳過0從1開始,直接是一、二、3……

那max_lo配置多大合適呢?

這要根據具體狀況而定,若是系統通常不重啓,並且須要用此表創建大量的主鍵,能夠吧max_lo配置大一點,這樣能夠減小讀取數據表的次數,提升效率;反之,若是服務器常常重啓,能夠吧max_lo配置小一點,能夠避免每次重啓主鍵之間的間隔太大,形成主鍵值主鍵不連貫。

特色:跨數據庫,hilo算法生成的標誌只能在一個數據庫中保證惟一。

四、seqhilo

與hilo相似,經過hi/lo算法實現的主鍵生成機制,只是將hilo中的數據表換成了序列sequence,須要數據庫中先建立sequence,適用於支持sequence的數據庫,如Oracle。

<id name="id" column="id">
<generator class="seqhilo">
<param name="sequence">hibernate_seq</param>
<param name="max_lo">100</param>
</generator>
</id>

 

 

特色:與hilo相似,只能在支持序列的數據庫中使用。

五、sequence

採用數據庫提供的sequence機制生成主鍵,須要數據庫支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL這種不支持sequence的數據庫則不行(可使用identity)。

<generator class="sequence">
<param name="sequence">hibernate_id</param>
</generator>
<param name="sequence">hibernate_id</param> 指定sequence的名稱

 

Hibernate生成主鍵時,查找sequence並賦給主鍵值,主鍵值由數據庫生成,Hibernate不負責維護,使用時必須先建立一個sequence,若是不指定sequence名稱,則使用Hibernate默認的sequence,名稱爲hibernate_sequence,前提要在數據庫中建立該sequence。

特色:只能在支持序列的數據庫中使用,如Oracle。

六、identity

identity由底層數據庫生成標識符。identity是由數據庫本身生成的,但這個主鍵必須設置爲自增加,使用identity的前提條件是底層數據庫支持自動增加字段類型,如DB二、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle這類沒有自增字段的則不支持。

<id name="id" column="id">
<generator class="identity" />
</id>

 

例:若是使用MySQL數據庫,則主鍵字段必須設置成auto_increment。

id int(11) primary key auto_increment

特色:只能用在支持自動增加的字段數據庫中使用,如MySQL。

七、native

native由hibernate根據使用的數據庫自行判斷採用identity、hilo、sequence其中一種做爲主鍵生成方式,靈活性很強。若是能支持identity則使用identity,若是支持sequence則使用sequence。

<id name="id" column="id">
<generator class="native" />
</id>

 

例如MySQL使用identity,Oracle使用sequence

注意:若是Hibernate自動選擇sequence或者hilo,則全部的表的主鍵都會從Hibernate默認的sequence或hilo表中取。而且,有的數據庫對於默認狀況主鍵生成測試的支持,效率並非很高。

使用sequence或hilo時,能夠加入參數,指定sequence名稱或hi值表名稱等,如

<param name="sequence">hibernate_id</param>

特色:根據數據庫自動選擇,項目中若是用到多個數據庫時,可使用這種方式,使用時須要設置表的自增字段或創建序列,創建表等。

八、uuid

UUID:Universally Unique Identifier,是指在一臺機器上生成的數字,它保證對在同一時空中的全部機器都是惟一的。按照開放軟件基金會(OSF)制定的標準計算,用到了以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字,標準的UUID格式爲:

xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)

其中每一個 x 是 0-9 或 a-f 範圍內的一個十六進制的數字。

配置文件的方式:

<id name="id" column="id">
<generator class="uuid" />
</id>

 

註解的方式:

   @Id
    @Column(length = 32)
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid")

 

Hibernate在保存對象時,生成一個UUID字符串做爲主鍵,保證了惟一性,但其並沒有任何業務邏輯意義,只能做爲主鍵,惟一缺點長度較大,32位(Hibernate將UUID中間的「-」刪除了)的字符串,佔用存儲空間大,可是有兩個很重要的優勢,Hibernate在維護主鍵時,不用去數據庫查詢,從而提升效率,並且它是跨數據庫的,之後切換數據庫極其方便。

特色:uuid長度大,佔用空間大,跨數據庫,不用訪問數據庫就生成主鍵值,因此效率高且能保證惟一性,移植很是方便,推薦使用。

九、guid

GUID:Globally Unique Identifier全球惟一標識符,也稱做 UUID,是一個128位長的數字,用16進製表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成GUID。從理論上講,若是一臺機器每秒產生10000000個GUID,則能夠保證(機率意義上)3240年不重複。

<id name="id" column="id">
<generator class="guid" />
</id>

 

Hibernate在維護主鍵時,先查詢數據庫,得到一個uuid字符串,該字符串就是主鍵值,該值惟一,缺點長度較大,支持數據庫有限,優勢同uuid,跨數據庫,可是仍然須要訪問數據庫。

注意:長度因數據庫不一樣而不一樣

MySQL中使用select uuid()語句得到的爲36位(包含標準格式的「-」)

Oracle中,使用select rawtohex(sys_guid()) from dual語句得到的爲32位(不包含「-」) 

特色:須要數據庫支持查詢uuid,生成時須要查詢數據庫,效率沒有uuid高,推薦使用uuid。

十、foreign

使用另一個相關聯的對象的主鍵做爲該對象主鍵。主要用於一對一關係中。

<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<one-to-one name="user" class="domain.User" constrained="true" />

 

該例使用domain.User的主鍵做爲本類映射的主鍵。

特色:不多使用,大多用在一對一關係中。

十一、select

使用觸發器生成主鍵,主要用於早期的數據庫主鍵生成機制,能用到的地方很是少。

十二、其餘註釋方式配置

註釋方式與配置文件底層實現方式相同,只是配置的方式換成了註釋方式

自動增加,適用於支持自增字段的數據庫

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

 

根據底層數據庫自動選擇方式,須要底層數據庫的設置

如MySQL,會使用自增字段,須要將主鍵設置成auto_increment。

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

 

使用表存儲生成的主鍵,能夠跨數據庫。

每次須要主鍵值時,查詢名爲"hibernate_table"的表,查找主鍵列"gen_pk"值爲"2"記錄,獲得這條記錄的"gen_val"值,根據這個值,和allocationSize的值生成主鍵值。

@Id

@GeneratedValue(strategy = GenerationType.TABLE, generator = "ud")

@TableGenerator(name = "ud",

table = "hibernate_table",

pkColumnName = "gen_pk",

pkColumnValue = "2",

valueColumnName = "gen_val",

initialValue = 2,

allocationSize = 5)

 

使用序列存儲主鍵值

@Id

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

@SequenceGenerator(name = "ud",

sequenceName = "hibernate_seq",

allocationSize = 1,

initialValue = 2)

 

1三、小結

1、爲了保證對象標識符的惟一性與不可變性,應該讓Hibernate來爲主鍵賦值,而不是程序。

2、正常使用Hibernate維護主鍵,最好將主鍵的setter方法設置成private,從而避免人爲或程序修改主鍵,而使用assigned方式,就不能用private,不然沒法給主鍵賦值。

二、Hibernate中惟一一種最簡單通用的主鍵生成器就是uuid。雖然是個32位難讀的長字符串,可是它沒有跨數據庫的問題,未來切換數據庫極其簡單方便,推薦使用!

三、自動增加字段類型與序列

數據庫

自動增加字段

序列

MySQL

 

Oracle

 

DB2

MS SQL Server

 

Sybase

 

HypersonicSQL

 

PostgreSQL

 

SAP DB

 

HSQLDB

 

Infomix

 

四、關於hilo機制注意:

hilo算法生成的標誌只能在一個數據庫中保證惟一。

當用戶爲Hibernate自行提供鏈接,或者Hibernate經過JTA,從應用服務器的數據源獲取數據庫鏈接時,沒法使用hilo,由於這不能保證hilo單獨在新的數據庫鏈接的事務中訪問hi值表,這種狀況,若是數據庫支持序列,可使用seqhilo。

五、使用identity、native、GenerationType.AUTO等方式生成主鍵時,只要用到自增字段,數據庫表的字段必須設置成自動增長的,不然出錯。

六、還有一些方法未列出來,例如uuid.hex,sequence-identity等,這些方法不是很經常使用,且已被其餘方法代替,如uuid.hex,官方文檔裏建議不使用,而直接使用uuid方法。

七、Hibernate的各版本主鍵生成策略配置有略微差異,但實現基本相同。如,有的版本默認sequence不指定序列名,則使用名爲hibernate_sequence的序列,有的版本則必須指定序列名。

八、還能夠自定義主鍵生成策略,這裏暫時不討論,只討論官方自帶生成策略。

相關文章
相關標籤/搜索