1、native-重點
native由hibernate根據使用的數據庫自行判斷采用identity、hilo、sequence其中一種作為主鍵生成方式,靈活性很強。如果能支持identity則使用identity,如果支持sequence則使用sequence。
數據庫ID必須設置為自動增長列,不能手動賦值(數據類型必須是int類型);當數據庫為int類型時,最好使用主鍵類型為native(能夠跨數據庫)
<id name="id" column="id">
<!--MySQL使用identity(id主鍵需要設置自增),Oracle使用sequence(hibernate默認查找hibernate_sequence序列,如果沒有需要自己創建)-->
<generator class="native" />
</id>
例如MySQL使用identity,Oracle使用sequence
MySQL使用identity: id主鍵必須自增類型
id INT UNSIGNED AUTO_INCREMENT
注意:如果Hibernate自動選擇sequence或者hilo,則所有的表的主鍵都會從Hibernate默認的sequence或hilo表中取。并且,有的數據庫對于默認情況主鍵生成測試的支持,效率并不是很高。
使用sequence或hilo時,可以加入參數,指定sequence名稱或hi值表名稱等,如
<id name="id" column="id" type="int">
<!--根據數據庫自動選擇identity、hilo、sequence其中一種,例如MySQL使用identity,Oracle使用sequence(默認序列查找hibernate_sequence序列),使用自定義序列時需加入參數sequence_name-->
<generator class="native">
<!--SEQ_MY_HIBERNATE 是自定義創建的序列-->
<param name="sequence_name">SEQ_MY_HIBERNATE</param>
</generator>
</id>
oracle創建序列
--oracle創建序列語句
--創建序列 SEQ_MY_HIBERNATE
create sequence SEQ_MY_HIBERNATE
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 20;
--創建序列 hibernate_sequence
create sequence hibernate_sequence
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 20;
特點:根據數據庫自動選擇,項目中如果用到多個數據庫時,可以使用這種方式,使用時需要設置表的自增字段或建立序列,建立表等。
2、increment-重點
由Hibernate從數據庫中取出主鍵的最大值(每個session只取1次),以該值為基礎,每次增量為1,在內存中生成主鍵,不依賴于底層的數據庫,因此可以跨數據庫。
數據庫ID可不設置為自動增長列,不能手動賦值(數據類型必須為int類型);
<id name="id" column="id" type="int">
<!--在內存中生成主鍵,不依賴于底層的數據庫,因此可以跨數據庫,首次從數據庫取主鍵最大的值-->
<generator class="increment" />
</id>
Hibernate調用org.hibernate.id.IncrementGenerator類里面的generate()方法,使用 select max(idColumnName) from tableName 語句獲取主鍵最大值。該方法被聲明成了synchronized,所以在一個獨立的Java虛擬機內部是沒有問題的,然而,在多個JVM同時并發訪問數據庫select max時就可能取出相同的值,再insert就會發生Dumplicate entry的錯誤。所以只能有一個Hibernate應用進程訪問數據庫,否則就可能產生主鍵沖突,所以不適合多進程并發更新數據庫,適合單一進程訪問數據庫,不能用于群集環境。
官方文檔:只有在沒有其他進程往同一張表中插入數據時才能使用,在集群下不要使用。
特點:跨數據庫,不適合多進程并發更新數據庫,適合單一進程訪問數據庫,不能用于群集環境。
3、sequence-重點
采用數據庫提供的sequence機制生成主鍵,需要數據庫支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL這種不支持sequence的數據庫則不行(可以使用identity)。
采用數據庫提供的sequence機制生成主鍵。MySql數據庫不支持。(數據類型必須是int類型)。
<id name="id" column="id" type="int">
<!--使用sequence,hibernate默認查找hibernate_sequence序列,如果沒有需要自己創建序列-->
<generator class="sequence">
<!--SEQ_MY_HIBERNATE 是自定義創建的序列,指定sequence的名稱-->
<param name="sequence_name">SEQ_MY_HIBERNATE</param>
</generator>
</id>
oracle創建序列
--oracle創建序列語句
--創建序列 SEQ_MY_HIBERNATE
create sequence SEQ_MY_HIBERNATE
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 20;
--創建序列 hibernate_sequence
create sequence hibernate_sequence
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 20;
Hibernate生成主鍵時,查找sequence并賦給主鍵值,主鍵值由數據庫生成,Hibernate不負責維護,使用時必須先創建一個sequence,如果不指定sequence名稱,則使用Hibernate默認的sequence,名稱為hibernate_sequence,前提要在數據庫中創建該sequence。
特點:只能在支持序列的數據庫中使用,如Oracle。
4、identity-重點
identity由底層數據庫生成標識符。identity是由數據庫自己生成的,但這個主鍵必須設置為自增長,使用identity的前提條件是底層數據庫支持自動增長字段類型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle這類沒有自增字段的則不支持。
數據庫ID必須設置為自動增長列,不能手動賦值(數據類型必須為int類型)。
<id name="id" column="id">
<generator class="identity" />
</id>
例:如果使用MySQL數據庫,則主鍵字段必須設置成auto_increment。
id int(11) primary key auto_increment
特點:只能用在支持自動增長的字段數據庫中使用,如MySQL。
5、uuid-掌握
UUID:Universally Unique Identifier,是指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。按照開放軟件基金會(OSF)制定的標準計算,用到了以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字,標準的UUID格式為:
xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
其中每個 x 是 0-9 或 a-f 范圍內的一個十六進制的數字。
數據庫ID不能設置為自動增長列,不能手動賦值(數據類型必須是String類型);生成的是一個16進制的數字(不能跨數據庫)。
<id name="id" column="id">
<generator class="uuid" />
</id>
Hibernate在保存對象時,生成一個UUID字符串作為主鍵,保證了唯一性,但其并無任何業務邏輯意義,只能作為主鍵,唯一缺點長度較大,32位(Hibernate將UUID中間的“-”刪除了)的字符串,占用存儲空間大,但是有兩個很重要的優點,Hibernate在維護主鍵時,不用去數據庫查詢,從而提高效率,而且它是跨數據庫的,以后切換數據庫極其方便。
特點:uuid長度大,占用空間大,跨數據庫,不用訪問數據庫就生成主鍵值,所以效率高且能保證唯一性,移植非常方便,推薦使用。
6、guid-掌握
GUID:Globally Unique Identifier全球唯一標識符,也稱作 UUID,是一個128位長的數字,用16進制表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成GUID。從理論上講,如果一臺機器每秒產生10000000個GUID,則可以保證(概率意義上)3240年不重復。
數據庫ID不能設置為自動增長列,不能手動賦值(數據類型必須是String類型);
當數據庫為String類型時,最好使用主鍵類型為guid。
<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。
7、assigned-掌握
主鍵由外部程序負責生成,在 save() 之前必須指定一個。Hibernate不負責維護主鍵生成。與Hibernate和底層數據庫都無關,可以跨數據庫。在存儲對象前,必須要使用主鍵的setter方法給主鍵賦值,至于這個值怎么生成,完全由自己決定,這種方法應該盡量避免。
數據庫ID設為自動增長列時,在存儲對象前,主鍵可手動賦值,也可不手動賦值; 不賦值執行數據庫自動增長,賦值執行賦予的值(數據類型為int)。
<id name="id" column="id">
<generator class="assigned" />
</id>
特點:可以跨數據庫,人為控制主鍵生成,應盡量避免。
8、hilo-掌握
hilo(高低位方式high low)是hibernate中最常用的一種生成方式,需要一張額外的表保存hi的值。保存hi值的表至少有一條記錄(只與第一條記錄有關),否則會出現錯誤。可以跨數據庫。
<id name="id" column="id">
<generator class="hilo">
<!--指定保存hi值的表名-->
<param name="table">hibernate_hilo</param>
<!--指定保存hi值的列名-->
<param name="column">next_hi</param>
<!--指定低位的最大值-->
<param name="max_lo">100</param>
</generator>
</id>
也可以省略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_hilo表,next_hi列為例):
獲得hi值:讀取并記錄數據庫的hibernate_hilo表中next_hi字段的值,數據庫中此字段值加1保存。
獲得lo值:從0到max_lo循環取值,差值為1,當值為max_lo值時,重新獲取hi值,然后lo值繼續從0到max_lo循環。
根據公式 hi * (max_lo + 1) 計算生成主鍵值。
注意:當hi值是0的時候,那么第一個值不是0*(max_lo+1)+0=0,而是lo跳過0從1開始,直接是1、2、3……
那max_lo配置多大合適呢?
這要根據具體情況而定,如果系統一般不重啟,而且需要用此表建立大量的主鍵,可以吧max_lo配置大一點,這樣可以減少讀取數據表的次數,提高效率;反之,如果服務器經常重啟,可以吧max_lo配置小一點,可以避免每次重啟主鍵之間的間隔太大,造成主鍵值主鍵不連貫。
特點:跨數據庫,hilo算法生成的標志只能在一個數據庫中保證唯一。
9、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類似,只能在支持序列的數據庫中使用。
10、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的主鍵作為本類映射的主鍵。
特點:很少使用,大多用在一對一關系中。
11、select
使用觸發器生成主鍵,主要用于早期的數據庫主鍵生成機制,能用到的地方非常少
12、自定義主鍵生成器
創建主鍵生成器類,實現 org.hibernate.id.IdentifierGenerator 接口。
示例1:讀取數據庫信息來實現自定義主鍵生成器(缺點:查詢了數據庫表中的所有數據,讀取速度慢):
public class MyGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session2 = sessionFactory.openSession();
Transaction transaction = session2.beginTransaction();
// 得到數據庫集合
List<Userinfo> lu = session2.createCriteria(Userinfo.class).list();
String str = "";
if (lu.size() > 0) {
for (int i = 0; i < lu.size(); i++) {
str = "Chain_0" + (i + 2);
}
} else {
str = "Chain_01";
}
transaction.commit();
session2.close();
sessionFactory.close();
return str;
}
}
示例2:讀取數據庫信息來實現自定義主鍵生成器(優點:對數據庫的表只查詢了一次,讀取速度相對于示例1較快):
public class MyGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
// 定義開始字符
String frist = "Chain_";
// 連接對象
Connection connection = session.connection();
try {
// 數據庫sql語句:查詢表的總數據
PreparedStatement ps = connection.prepareStatement("SELECT COUNT(*) AS nextval FROM userinfo");
// 返回結果集
ResultSet rs = ps.executeQuery();
if (rs.next()) {
// 得到查詢的值+1
int id = rs.getInt("nextval") + 1;
String code = frist + StringUtils.leftPad("" + id, 3, "0");
return code;
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
在hibernate.xml里面設置:
<id name="id" type="java.lang.String">
<column name="ID" />
<generator class="com.zja.hibernate.generator.MyGenerator" />
</id>
13、Hibernate jpa 主鍵策略
Hibernate的主鍵策略jpa都能使用,另外jpa自己也提供了四種主鍵生成策略。
在jpa中我們可以通過注解@GeneratedValue(strategy=GenerationType.IDENTITY)來設置主鍵生成策略,但是jpa中的主鍵生成策略有不完美的地方,那就是只能設置數值型主鍵的生成策略,而對于字符串類型的主鍵不能處理。但是jpa給我們了一個生成器,實現jpa規范的框架可以通過生成器設置主鍵生成策略。
jpa 通過注解的方式來配置主鍵生成策略:配置uuid的生成器,生成主鍵的時候則會按照此方式生成
@Id
@Column(name="cust_id")
@GenericGenerator(name="my_uuid",strategy="uuid")// 定義一個生成器
@GeneratedValue(generator="my_uuid")//指定生成器
private String custId;
JPA自己提供了四種的主鍵生成策略:
- AUTO:主鍵由程序控制。
- IDENTITY:主鍵由數據庫自動生成(主要是自動增長型)
- SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。
- TABLE:使用一個特定的數據庫表格來保存主鍵。
-
AUTO
默認的配置。如果不指定主鍵生成策略,默認為AUTO。
GenerationType.AUTO:把主鍵生成策略交給持久化引擎(persistence engine),持久化引擎會根據數據庫在以上三種主鍵生成策略中選擇其中一種。此種主鍵生成策略比較常用,由于JPA默認的生成策略就是GenerationType.AUTO,所以使用此種策略時.可以顯式的指定@GeneratedValue(strategy = GenerationType.AUTO)也可以直接@GeneratedValue。例如:
@Id
@GeneratedValue
或
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
- IDENTITY
? 主鍵則由數據庫自動維護,使用起來很簡單。
GenerationType.IDENTITY:此種主鍵生成策略就是通常所說的主鍵自增長,數據庫在插入數據時,會自動給主鍵賦值,比如MYSQL可以在創建表時聲明"auto_increment" 來指定主鍵自增長。該策略在大部分數據庫中都提供了支持(指定方法或關鍵字可能不同),但還是有少數數據庫不支持,所以可移植性略差。使用自增長主鍵生成策略是只需要聲明strategy = GenerationType.IDENTITY即可。例如:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
-
SEQUENCE
Hibernate同時也對JPA進行了擴展,可以在 GeneratedValue 中指定 generator,然后用 GenericGenerator 指定策略來維護主鍵。
GenerationType.SEQUENCE:在某些數據庫中,不支持主鍵自增長,比如Oracle,其提供了一種叫做"序列(sequence)"的機制生成主鍵。此時,GenerationType.SEQUENCE就可以作為主鍵生成策略。該策略的不足之處正好與TABLE相反,由于只有部分數據庫(Oracle,PostgreSQL,DB2)支持序列對象,所以該策略一般不應用于其他數據庫。類似的,該策略一般與另外一個注解一起使用@SequenceGenerator,@SequenceGenerator注解指定了生成主鍵的序列.然后JPA會根據注解內容創建一個序列(或使用一個現有的序列)。如果不指定序列,則會自動生成一個序列SEQ_GEN_SEQUENCE。例如:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_sequence")
@SequenceGenerator(name = "id_sequence", initialValue = 1, allocationSize = 1, sequenceName = "ID_SEQUENCE")
private int id;
- TABLE
GenerationType.TABLE:使用一個特定的數據庫表格來保存主鍵,持久化引擎通過關系數據庫的一張特定的表格來生成主鍵,這種策略的好處就是不依賴于外部環境和數據庫的具體實現,在不同數據庫間可以很容易的進行移植,但由于其不能充分利用數據庫的特性,所以不會優先使用。該策略一般與另外一個注解一起使用@TableGenerator,@TableGenerator注解指定了生成主鍵的表(可以在實體類上指定也可以在主鍵字段或屬性上指定),然后JPA將會根據注解內容自動生成一張表作為序列表(或使用現有的序列表)。如果不指定序列表,則會生成一張默認的序列表,表中的列名也是自動生成,數據庫上會生成一張名為sequence的表(SEQ_NAME,SEQ_COUNT)。序列表一般只包含兩個字段:第一個字段是該生成策略的名稱,第二個字段是該關系表的最大序號,它會隨著數據的插入逐漸累加。例如:
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "id_sequence")
@TableGenerator(name = "id_sequence", allocationSize = 1, table = "sequence_table", pkColumnName = "sequence_max_id", valueColumnName = "sequence_count")
private int id;
補充說明:
@GeneratedValue: 給實體類生成唯一標識的主鍵(JPA要求實體Entity,必須有且只有一個主鍵)。有兩個屬性,分別是strategy和generator。
generator:默認為空字符串,定義了主鍵生成器的名稱,對應的生成器有兩個:對應于同名的主鍵生成器@SequenceGenerator和@TableGenerator。
strategy:一共有四種,被定義在枚舉類GenerationType中,包括:TABLE, SEQUENCE, IDENTITY 和AUTO。
@GenericGenerator: ** 是HIbernate提供的自定義主鍵策略生成器,需要和@GeneratedValue**一起使用,它的name屬性要和@GeneratedValue中的generator名稱要一致。支持13種策略。
@Id
@GeneratedValue(generator = "myGenerator")
@GenericGenerator(name = "myGenerator", strategy = "uuid")
其中 strategy 有15個選項(13種策略),分別是
public DefaultIdentifierGeneratorFactory() {
//uuid2特點是:不需要和數據庫交互,可根據RFC4122定義的5中變量控制具體的生成策略(因為符合RFC4122定義,所以避免了警告信息)
register("uuid2", UUIDGenerator.class);
//guid特點是:需要和數據庫進行一次查詢才能生成。數據庫全局唯一。MySQL 用 select uuid();Oracle 用 return "select rawtohex(sys_guid()) from dual";
register("guid", GUIDGenerator.class);
//uuid特點:不需要和數據庫交互,全網唯一。它會根據內部程序計算出32位長度的唯一id
register("uuid", UUIDHexGenerator.class);
register("uuid.hex", UUIDHexGenerator.class);
//hilo特點:需要和數據庫交互,全數據庫唯一,與guid不同的是,在標識符的單個源必須被多個插入訪問時可以避免擁堵。
register("hilo", TableHiLoGenerator.class);
//assigned特點:不需要和數據庫交互,自己管理主鍵生成,顯示的指定id。沒有生成邏輯,如果為空就拋出異常。
register("assigned", Assigned.class);
//identity特點:需要和數據庫交互,數據插入后返回(反查)id,同一列唯一。
register("identity", IdentityGenerator.class);
//select特點:需要和數據庫交互。具有和identity類似的行為,有數據庫觸發器生成。
register("select", SelectGenerator.class);
//sequence特點:需要和數據庫交互(但不是每次都是)。sequence唯一。通過不同的數據庫,獲取不同的取值語句 dialect.getSequenceNextValString(sequenceName); 然后進行查詢,緩存到IntegralDataTypeHolder中,通過 generateHolder( session ).makeValue();進行獲得.
register("sequence", SequenceGenerator.class);
//seqhilo特點:需要和數據庫交互,全數據庫唯一,與guid不同的是,在標識符的單個源必須被多個插入訪問時可以避免擁堵。處理邏輯和 hilo 相同,只不過是使用一個具名的數據庫序列來生成高值部分
register("seqhilo", SequenceHiLoGenerator.class);
//increment特點:僅需要首次訪問數據庫。數據庫啟動時查詢表的最大主鍵列支,并通過 IntegralDataTypeHolder 緩存。插入一條,它自加一。不適合集群項目。
register("increment", IncrementGenerator.class);
//foreign特點:需要和數據庫訪問。通過給定的 entityName 和 propertyName 查詢獲得值
register("foreign", ForeignGenerator.class);
register("sequence-identity", SequenceIdentityGenerator.class);
register("enhanced-sequence", SequenceStyleGenerator.class);
register("enhanced-table", TableGenerator.class);
}