主鍵由外部程式負責生成,在 save() 之前必須指定一個。Hibernate不負責維護主鍵生成。與Hibernate和底層資料庫都無關,可以跨資料庫。在存儲對象前,必須要使用主鍵的setter方法給主鍵指派,至于這個值怎麼生成,完全由自己決定,這種方法應該盡量避免。
<id name="id" column="id">
<generator class="assigned" />
</id>
“ud”是自定義的政策名,人為起的名字,後面均用“ud”表示。
特點:可以跨資料庫,人為控制主鍵生成,應盡量避免。
由Hibernate從資料庫中取出主鍵的最大值(每個session隻取1次),以該值為基礎,每次增量為1,在記憶體中生成主鍵,不依賴于底層的資料庫,是以可以跨資料庫。
<generator class="increment" />
Hibernate調用org.hibernate.id.IncrementGenerator類裡面的generate()方法,使用select max(idColumnName) from tableName語句擷取主鍵最大值。該方法被聲明成了synchronized,是以在一個獨立的Java虛拟機内部是沒有問題的,然而,在多個JVM同時并發通路資料庫select max時就可能取出相同的值,再insert就會發生Dumplicate entry的錯誤。是以隻能有一個Hibernate應用程序通路資料庫,否則就可能産生主鍵沖突,是以不适合多程序并發更新資料庫,适合單一程序通路資料庫,不能用于群集環境。
官方文檔:隻有在沒有其他程序往同一張表中插入資料時才能使用,在叢集下不要使用。
特點:跨資料庫,不适合多程序并發更新資料庫,适合單一程序通路資料庫,不能用于群集環境。
hilo(高低位方式high low)是hibernate中最常用的一種生成方式,需要一張額外的表儲存hi的值。儲存hi值的表至少有一條記錄(隻與第一條記錄有關),否則會出現錯誤。可以跨資料庫。
<generator class="hilo">
<param name="table">hibernate_hilo</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
<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
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開始,直接是1、2、3……
那max_lo配置多大合适呢?
這要根據具體情況而定,如果系統一般不重新開機,而且需要用此表建立大量的主鍵,可以吧max_lo配置大一點,這樣可以減少讀取資料表的次數,提高效率;反之,如果伺服器經常重新開機,可以吧max_lo配置小一點,可以避免每次重新開機主鍵之間的間隔太大,造成主鍵值主鍵不連貫。
特點:跨資料庫,hilo算法生成的标志隻能在一個資料庫中保證唯一。
與hilo類似,通過hi/lo算法實作的主鍵生成機制,隻是将hilo中的資料表換成了序列sequence,需要資料庫中先建立sequence,适用于支援sequence的資料庫,如Oracle。
<generator class="seqhilo">
<param name="sequence">hibernate_seq</param>
特點:與hilo類似,隻能在支援序列的資料庫中使用。
采用資料庫提供的sequence機制生成主鍵,需要資料庫支援sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL這種不支援sequence的資料庫則不行(可以使用identity)。
<generator class="sequence">
<param name="sequence">hibernate_id</param>
<param name="sequence">hibernate_id</param> 指定sequence的名稱
Hibernate生成主鍵時,查找sequence并賦給主鍵值,主鍵值由資料庫生成,Hibernate不負責維護,使用時必須先建立一個sequence,如果不指定sequence名稱,則使用Hibernate預設的sequence,名稱為hibernate_sequence,前提要在資料庫中建立該sequence。
特點:隻能在支援序列的資料庫中使用,如Oracle。
identity由底層資料庫生成辨別符。identity是由資料庫自己生成的,但這個主鍵必須設定為自增長,使用identity的前提條件是底層資料庫支援自動增長字段類型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle這類沒有自增字段的則不支援。
<generator class="identity" />
例:如果使用MySQL資料庫,則主鍵字段必須設定成auto_increment。
id int(11) primary key auto_increment
特點:隻能用在支援自動增長的字段資料庫中使用,如MySQL。
native由hibernate根據使用的資料庫自行判斷采用identity、hilo、sequence其中一種作為主鍵生成方式,靈活性很強。如果能支援identity則使用identity,如果支援sequence則使用sequence。
<generator class="native" />
例如MySQL使用identity,Oracle使用sequence
注意:如果Hibernate自動選擇sequence或者hilo,則所有的表的主鍵都會從Hibernate預設的sequence或hilo表中取。并且,有的資料庫對于預設情況主鍵生成測試的支援,效率并不是很高。
使用sequence或hilo時,可以加入參數,指定sequence名稱或hi值表名稱等,如
特點:根據資料庫自動選擇,項目中如果用到多個資料庫時,可以使用這種方式,使用時需要設定表的自增字段或建立序列,建立表等。
UUID:Universally Unique Identifier,是指在一台機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。按照開放軟體基金會(OSF)制定的标準計算,用到了以太網卡位址、納秒級時間、晶片ID碼和許多可能的數字,标準的UUID格式為:
xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
其中每個 x 是 0-9 或 a-f 範圍内的一個十六進制的數字。
<generator class="uuid" />
Hibernate在儲存對象時,生成一個UUID字元串作為主鍵,保證了唯一性,但其并無任何業務邏輯意義,隻能作為主鍵,唯一缺點長度較大,32位(Hibernate将UUID中間的“-”删除了)的字元串,占用存儲空間大,但是有兩個很重要的優點,Hibernate在維護主鍵時,不用去資料庫查詢,進而提高效率,而且它是跨資料庫的,以後切換資料庫極其友善。
特點:uuid長度大,占用空間大,跨資料庫,不用通路資料庫就生成主鍵值,是以效率高且能保證唯一性,移植非常友善,推薦使用。
GUID:Globally Unique Identifier全球唯一辨別符,也稱作 UUID,是一個128位長的數字,用16進制表示。算法的核心思想是結合機器的網卡、當地時間、一個随即數來生成GUID。從理論上講,如果一台機器每秒産生10000000個GUID,則可以保證(機率意義上)3240年不重複。
<generator class="guid" />
Hibernate在維護主鍵時,先查詢資料庫,獲得一個uuid字元串,該字元串就是主鍵值,該值唯一,缺點長度較大,支援資料庫有限,優點同uuid,跨資料庫,但是仍然需要通路資料庫。
注意:長度因資料庫不同而不同
MySQL中使用select uuid()語句獲得的為36位(包含标準格式的“-”)
Oracle中,使用select rawtohex(sys_guid()) from dual語句獲得的為32位(不包含“-”)
特點:需要資料庫支援查詢uuid,生成時需要查詢資料庫,效率沒有uuid高,推薦使用uuid。
使用另外一個相關聯的對象的主鍵作為該對象主鍵。主要用于一對一關系中。
<generator class="foreign">
<param name="property">user</param>
<one-to-one name="user" class="domain.User" constrained="true" />
該例使用domain.User的主鍵作為本類映射的主鍵。
特點:很少使用,大多用在一對一關系中。
使用觸發器生成主鍵,主要用于早期的資料庫主鍵生成機制,能用到的地方非常少。
注釋方式與配置檔案底層實作方式相同,隻是配置的方式換成了注釋方式
自動增長,适用于支援自增字段的資料庫
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
根據底層資料庫自動選擇方式,需要底層資料庫的設定
如MySQL,會使用自增字段,需要将主鍵設定成auto_increment。
@GeneratedValue(strategy = GenerationType.AUTO)
使用表存儲生成的主鍵,可以跨資料庫。
每次需要主鍵值時,查詢名為"hibernate_table"的表,查找主鍵列"gen_pk"值為"2"記錄,得到這條記錄的"gen_val"值,根據這個值,和allocationSize的值生成主鍵值。
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ud")
@TableGenerator(name = "ud",
table = "hibernate_table",
pkColumnName = "gen_pk",
pkColumnValue = "2",
valueColumnName = "gen_val",
initialValue = 2,
allocationSize = 5)
使用序列存儲主鍵值
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ud")
@SequenceGenerator(name = "ud",
sequenceName = "hibernate_seq",
allocationSize = 1,
initialValue = 2)
1、為了保證對象辨別符的唯一性與不可變性,應該讓Hibernate來為主鍵指派,而不是程式。
2、正常使用Hibernate維護主鍵,最好将主鍵的setter方法設定成private,進而避免人為或程式修改主鍵,而使用assigned方式,就不能用private,否則無法給主鍵指派。
2、Hibernate中唯一一種最簡單通用的主鍵生成器就是uuid。雖然是個32位難讀的長字元串,但是它沒有跨資料庫的問題,将來切換資料庫極其簡單友善,推薦使用!
3、自動增長字段類型與序列
資料庫
自動增長字段
序列
MySQL
是
Oracle
DB2
MS SQL Server
Sybase
HypersonicSQL
PostgreSQL
SAP DB
HSQLDB
Infomix
4、關于hilo機制注意:
hilo算法生成的标志隻能在一個資料庫中保證唯一。
當使用者為Hibernate自行提供連接配接,或者Hibernate通過JTA,從應用伺服器的資料源擷取資料庫連接配接時,無法使用hilo,因為這不能保證hilo單獨在新的資料庫連接配接的事務中通路hi值表,這種情況,如果資料庫支援序列,可以使用seqhilo。
5、使用identity、native、GenerationType.AUTO等方式生成主鍵時,隻要用到自增字段,資料庫表的字段必須設定成自動增加的,否則出錯。
6、還有一些方法未列出來,例如uuid.hex,sequence-identity等,這些方法不是很常用,且已被其他方法代替,如uuid.hex,官方文檔裡建議不使用,而直接使用uuid方法。
7、Hibernate的各版本主鍵生成政策配置有略微差别,但實作基本相同。如,有的版本預設sequence不指定序列名,則使用名為hibernate_sequence的序列,有的版本則必須指定序列名。
8、還可以自定義主鍵生成政策,這裡暫時不讨論,隻讨論官方自帶生成政策。