轉JPA實體註解與hibernate主鍵生成策略
JPA實體註解與hibernate主鍵生成策略
用hibernate註解開發專案,對於主鍵的生成策略有些模糊,下面是從新浪網裡面看到的一篇關於hibernate註解以及主鍵生成策略的文章,值得一看:
1. 實體標誌:
@Entity
@Indexed(index="group")
@Table(name="GROUP_LAYMOD")
* @Indexed 標識需要進行索引的物件,
* 屬性 : index 指定索引檔案的路徑 @DocumentId 用於標示實體類中的唯一的屬性儲存在索引檔案中,是當進行全文檢索時可以這個唯一的屬性來區分索引中其他實體物件,一般使用實體類中的主鍵屬性
* @Field 標註在類的get屬性上,標識一個索引的Field
屬性 : index 指定是否索引,與Lucene相同
store 指定是否索引,與Lucene相同
name 指定Field的name,預設為類屬性的名稱
analyzer 指定分析器
在hibernate註解主鍵的時候,一般會使用到這兩個。
@GeneratedValue的作用是JPA的預設實現自定義主鍵生成策略
@GenericGenerator是hibernate在JPA的基礎上增強。
自定義主鍵生成策略,由@GenericGenerator實現。
hibernate在JPA的基礎上進行了擴充套件,可以用一下方式引入hibernate獨有的主鍵生成策略,就是通過@GenericGenerator加入的。
比如說,JPA標準用法
@Id
@GeneratedValue(GenerationType.AUTO)
就可以用hibernate特有以下用法來實現
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
@GenericGenerator的定義:
@Target({PACKAGE, TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface GenericGenerator {
String name();
String strategy();
Parameter[] parameters() default {};
}
name屬性指定生成器名稱。
strategy屬性指定具體生成器的類名。
parameters得到strategy指定的具體生成器所用到的引數。
對於這些hibernate主鍵生成策略和各自的具體生成器之間的關係,在org.hibernate.id.IdentifierGeneratorFactory中指定了,
static {
GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
}
上面十二種策略,加上native,hibernate一共預設支援十三種生成策略。
1、native
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "native")
2、uuid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
3、hilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "hilo")
4、assigned
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
5、identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "identity")
6、select
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name="select", strategy="select",
parameters = { @Parameter(name = "key", value = "idstoerung") })
7、sequence
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
8、seqhilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "seqhilo",
parameters = { @Parameter(name = "max_lo", value = "5") })
9、increment
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "increment")
10、foreign
Java程式碼
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
注意:直接使用@PrimaryKeyJoinColumn 報錯(?)
Java程式碼
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
例如
Java程式碼
@Entity
public class Employee {
@Id Integer id;
@OneToOne @PrimaryKeyJoinColumn
EmployeeInfo info;
}
應該為
Java程式碼
@Entity
public class Employee {
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "info") })
Integer id;
@OneToOne
EmployeeInfo info;
}
11、guid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "guid")
12、uuid.hex
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid.hex")
13、sequence-identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence-identity",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
四、通過@GenericGenerator自定義主鍵生成策略
如果實際應用中,主鍵策略為程式指定了就用程式指定的主鍵(assigned),沒有指定就從sequence中取。
明顯上面所討論的策略都不滿足,只好自己擴充套件了,整合assigned和sequence兩種策略。
Java程式碼
public class AssignedSequenceGenerator extends SequenceGenerator implements
PersistentIdentifierGenerator, Configurable {
private String entityName;
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
entityName = params.getProperty(ENTITY_NAME);
if (entityName==null) {
throw new MappingException("no entity name");
}
super.configure(type, params, dialect);
}
public Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
Serializable id = session.getEntityPersister( entityName, obj )
.getIdentifier( obj, session.getEntityMode() );
if (id==null) {
id = super.generate(session, obj);
}
return id;
}
}
實際應用中,定義同sequence。
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "AssignedSequenceGenerator",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
值得注意的是,定義的這種策略,就像開啟了潘多拉魔盒,非常不可控。正常情況下,不建議這麼做。
策略解釋
“assigned”
主鍵由外部程式負責生成,在 save() 之前指定一個。
“hilo”
通過hi/lo 演算法實現的主鍵生成機制,需要額外的資料庫表或欄位提供高位值來源
“seqhilo”
與hilo 類似,通過hi/lo 演算法實現的主鍵生成機制,需要資料庫中的 Sequence,適用於支援 Sequence 的資料庫,如Oracle。
“increment”
主鍵按數值順序遞增。此方式的實現機制為在當前應用例項中維持一個變數,以儲存著當前的最大值,之後每次需要生成主鍵的時候將此值加1作為主鍵。這種方式可能產生的問題是:不能在叢集下使用。
“identity”
採用資料庫提供的主鍵生成機制。如DB2、SQL Server、MySQL 中的主鍵生成機制。
“sequence”
採用資料庫提供的 sequence 機制生成主鍵。如 Oralce 中的Sequence。
“native”
由 Hibernate 根據使用的資料庫自行判斷採用 identity、hilo、sequence 其中一種作為主鍵生成方式。
“uuid.hex”
由 Hibernate 基於128 位 UUID 演算法 生成16 進位制數值(編碼後以長度32 的字串表示)作為主鍵。
“uuid.string”
與uuid.hex 類似,只是生成的主鍵未進行編碼(長度16),不能應用在 PostgreSQL 資料庫中。
“foreign”
使用另外一個相關聯的物件的識別符號作為主鍵。
在 IBM Bluemix 雲平臺上開發並部署您的下一個應用。
開始您的試用
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 策略生成主鍵
@Entity
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 專案持久層開發中以不變應萬變的姿態來面對紛繁複雜的具體情況了。
一、JPA通用策略生成器
通過annotation來對映hibernate實體的,基於annotation的hibernate主鍵標識為@Id,
其生成規則由@GeneratedValue設定的.這裡的@id和@GeneratedValue都是JPA的標準用法,
JPA提供四種標準用法,由@GeneratedValue的原始碼可以明顯看出.
Java程式碼
@Target({METHOD,FIELD})
@Retention(RUNTIME)
public @interface GeneratedValue{
GenerationType strategy() default AUTO;
String generator() default "";
}
其中GenerationType:
Java程式碼
public enum GenerationType{
TABLE,
SEQUENCE,
IDENTITY,
AUTO
}
JPA提供的四種標準用法為TABLE,SEQUENCE,IDENTITY,AUTO.
TABLE:使用一個特定的資料庫表格來儲存主鍵。
SEQUENCE:根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列。
IDENTITY:主鍵由資料庫自動生成(主要是自動增長型)
AUTO:主鍵由程式控制。
1、TABLE
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
這裡應用表tb_generator,定義為
Sql程式碼
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
插入紀錄,供生成主鍵使用,
Sql程式碼
INSERT INTO tb_generator(id, gen_name, gen_value) VALUES (1,PAYABLEMOENY_PK', 1);
在主鍵生成後,這條紀錄的value值,按allocationSize遞增。
@TableGenerator的定義:
Java程式碼
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
String name();
String table() default "";
String catalog() default "";
String schema() default "";
String pkColumnName() default "";
String valueColumnName() default "";
String pkColumnValue() default "";
int initialValue() default 0;
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
其中屬性說明:
name屬性表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設定的“generator”值中。
table屬性表示表生成策略所持久化的表名,例如,這裡表使用的是資料庫中的“tb_generator”。
catalog屬性和schema具體指定表所在的目錄名或是資料庫名。
pkColumnName屬性的值表示在持久化表中,該主鍵生成策略所對應鍵值的名稱。例如在“tb_generator”中將“gen_name”作為主鍵的鍵值
valueColumnName屬性的值表示在持久化表中,該主鍵當前所生成的值,它的值將會隨著每次建立累加。例如,在“tb_generator”中將“gen_value”作為主鍵的值
pkColumnValue屬性的值表示在持久化表中,該生成策略所對應的主鍵。例如在“tb_generator”表中,將“gen_name”的值為“CUSTOMER_PK”。
initialValue表示主鍵初識值,預設為0。
allocationSize表示每次主鍵值增加的大小,例如設定成1,則表示每次建立新記錄後自動加1,預設為50。
UniqueConstraint與@Table標記中的用法類似。
2、SEQUENCE
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq")
@SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment")
@SequenceGenerator定義
Java程式碼
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerator {
String name();
String sequenceName() default "";
int initialValue() default 0;
int allocationSize() default 50;
}
name屬性表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設定的“generator”值中。
sequenceName屬性表示生成策略用到的資料庫序列名稱。
initialValue表示主鍵初識值,預設為0。
allocationSize表示每次主鍵值增加的大小,例如設定成1,則表示每次建立新記錄後自動加1,預設為50。
3、IDENTITY
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
4、AUTO
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
在指定主鍵時,如果不指定主鍵生成策略,預設為AUTO。
Java程式碼
@Id
跟下面的定義是一樣的。
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
二、hibernate主鍵策略生成器
hibernate提供多種主鍵生成策略,有點是類似於JPA,有的是hibernate特有:
native: 對於 oracle 採用 Sequence 方式,對於MySQL 和 SQL Server 採用identity(自增主鍵生成機制),native就是將主鍵的生成工作交由資料庫完成,hibernate不管(很常用)。
uuid: 採用128位的uuid演算法生成主鍵,uuid被編碼為一個32位16進位制數字的字串。佔用空間大(字串型別)。
hilo: 使用hilo生成策略,要在資料庫中建立一張額外的表,預設表名為hibernate_unique_key,預設欄位為integer型別,名稱是next_hi(比較少用)。
assigned: 在插入資料的時候主鍵由程式處理(很常用),這是 <generator>元素沒有指定時的預設生成策略。等同於JPA中的AUTO。
identity: 使用SQL Server 和 MySQL 的自增欄位,這個方法不能放到 Oracle 中,Oracle 不支援自增欄位,要設定sequence(MySQL 和 SQL Server 中很常用)。
等同於JPA中的INDENTITY。
select: 使用觸發器生成主鍵(主要用於早期的資料庫主鍵生成機制,少用)。
sequence: 呼叫底層資料庫的序列來生成主鍵,要設定序列名,不然hibernate無法找到。
seqhilo: 通過hilo演算法實現,但是主鍵歷史儲存在Sequence中,適用於支援 Sequence 的資料庫,如 Oracle(比較少用)
increment: 插入資料的時候hibernate會給主鍵新增一個自增的主鍵,但是一個hibernate例項就維護一個計數器,所以在多個例項執行的時候不能使用這個方法。
foreign: 使用另外一個相關聯的物件的主鍵。通常和<one-to-one>聯合起來使用。
guid: 採用資料庫底層的guid演算法機制,對應MYSQL的uuid()函式,SQL Server的newid()函式,ORACLE的rawtohex(sys_guid())函式等。
uuid.hex: 看uuid,建議用uuid替換。
sequence-identity: sequence策略的擴充套件,採用立即檢索策略來獲取sequence值,需要JDBC3.0和JDK4以上(含1.4)版本
hibernate提供了多種生成器供選擇,基於Annotation的方式通過@GenericGenerator實現.
hibernate每種主鍵生成策略提供介面org.hibernate.id.IdentifierGenerator的實現類,如果要實現自定義的主鍵生成策略也必須實現此介面.
Java程式碼
public interface IdentifierGenerator {
/**
* The configuration parameter holding the entity name
*/
public static final String ENTITY_NAME = "entity_name";
/**
* Generate a new identifier.
* @param session
* @param object the entity or toplevel collection for which the id is being generated
*
* @return a new identifier
* @throws HibernateException
*/
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException;
}
IdentifierGenerator提供一generate方法,generate方法返回產生的主鍵.
三、@GenericGenerator
自定義主鍵生成策略,由@GenericGenerator實現。
hibernate在JPA的基礎上進行了擴充套件,可以用一下方式引入hibernate獨有的主鍵生成策略,就是通過@GenericGenerator加入的。
比如說,JPA標準用法
Java程式碼
@Id
@GeneratedValue(GenerationType.AUTO)
就可以用hibernate特有以下用法來實現
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
@GenericGenerator的定義:
Java程式碼
@Target({PACKAGE, TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface GenericGenerator {
/**
* unique generator name
*/
String name();
/**
* Generator strategy either a predefined Hibernate
* strategy or a fully qualified class name.
*/
String strategy();
/**
* Optional generator parameters
*/
Parameter[] parameters() default {};
}
name屬性指定生成器名稱。
strategy屬性指定具體生成器的類名。
parameters得到strategy指定的具體生成器所用到的引數。
對於這些hibernate主鍵生成策略和各自的具體生成器之間的關係,在org.hibernate.id.IdentifierGeneratorFactory中指定了,
Java程式碼
static {
GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
}
上面十二種策略,加上native,hibernate一共預設支援十三種生成策略。
1、native
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "native")
2、uuid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
3、hilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "hilo")
4、assigned
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
5、identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "identity")
6、select
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name="select", strategy="select",
parameters = { @Parameter(name = "key", value = "idstoerung") })
7、sequence
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
8、seqhilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "seqhilo",
parameters = { @Parameter(name = "max_lo", value = "5") })
9、increment
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "increment")
10、foreign
Java程式碼
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
注意:直接使用@PrimaryKeyJoinColumn 報錯(?)
Java程式碼
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
例如
Java程式碼
@Entity
public class Employee {
@Id Integer id;
@OneToOne @PrimaryKeyJoinColumn
EmployeeInfo info;
...
}
應該為
Java程式碼
@Entity
public class Employee {
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "info") })
Integer id;
@OneToOne
EmployeeInfo info;
...
}
11、guid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "guid")
12、uuid.hex
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid.hex")
13、sequence-identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence-identity",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
四、通過@GenericGenerator自定義主鍵生成策略
如果實際應用中,主鍵策略為程式指定了就用程式指定的主鍵(assigned),沒有指定就從sequence中取。
明顯上面所討論的策略都不滿足,只好自己擴充套件了,整合assigned和sequence兩種策略。
Java程式碼
public class AssignedSequenceGenerator extends SequenceGenerator implements
PersistentIdentifierGenerator, Configurable {
private String entityName;
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
entityName = params.getProperty(ENTITY_NAME);
if (entityName==null) {
throw new MappingException("no entity name");
}
super.configure(type, params, dialect);
}
public Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
Serializable id = session.getEntityPersister( entityName, obj )
.getIdentifier( obj, session.getEntityMode() );
if (id==null) {
id = super.generate(session, obj);
}
return id;
}
}
實際應用中,定義同sequence。
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "AssignedSequenceGenerator",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
值得注意的是,定義的這種策略,就像開啟了潘多拉魔盒,非常不可控。正常情況下,不建議這麼做。
用hibernate註解開發專案,對於主鍵的生成策略有些模糊,下面是從新浪網裡面看到的一篇關於hibernate註解以及主鍵生成策略的文章,值得一看:
1. 實體標誌:
@Entity
@Indexed(index="group")
@Table(name="GROUP_LAYMOD")
* @Indexed 標識需要進行索引的物件,
* 屬性 : index 指定索引檔案的路徑 @DocumentId 用於標示實體類中的唯一的屬性儲存在索引檔案中,是當進行全文檢索時可以這個唯一的屬性來區分索引中其他實體物件,一般使用實體類中的主鍵屬性
* @Field 標註在類的get屬性上,標識一個索引的Field
屬性 : index 指定是否索引,與Lucene相同
store 指定是否索引,與Lucene相同
name 指定Field的name,預設為類屬性的名稱
analyzer 指定分析器
在hibernate註解主鍵的時候,一般會使用到這兩個。
@GeneratedValue的作用是JPA的預設實現自定義主鍵生成策略
@GenericGenerator是hibernate在JPA的基礎上增強。
自定義主鍵生成策略,由@GenericGenerator實現。
hibernate在JPA的基礎上進行了擴充套件,可以用一下方式引入hibernate獨有的主鍵生成策略,就是通過@GenericGenerator加入的。
比如說,JPA標準用法
@Id
@GeneratedValue(GenerationType.AUTO)
就可以用hibernate特有以下用法來實現
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
@GenericGenerator的定義:
@Target({PACKAGE, TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface GenericGenerator {
String name();
String strategy();
Parameter[] parameters() default {};
}
name屬性指定生成器名稱。
strategy屬性指定具體生成器的類名。
parameters得到strategy指定的具體生成器所用到的引數。
對於這些hibernate主鍵生成策略和各自的具體生成器之間的關係,在org.hibernate.id.IdentifierGeneratorFactory中指定了,
static {
GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
}
上面十二種策略,加上native,hibernate一共預設支援十三種生成策略。
1、native
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "native")
2、uuid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
3、hilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "hilo")
4、assigned
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
5、identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "identity")
6、select
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name="select", strategy="select",
parameters = { @Parameter(name = "key", value = "idstoerung") })
7、sequence
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
8、seqhilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "seqhilo",
parameters = { @Parameter(name = "max_lo", value = "5") })
9、increment
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "increment")
10、foreign
Java程式碼
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
注意:直接使用@PrimaryKeyJoinColumn 報錯(?)
Java程式碼
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
例如
Java程式碼
@Entity
public class Employee {
@Id Integer id;
@OneToOne @PrimaryKeyJoinColumn
EmployeeInfo info;
}
應該為
Java程式碼
@Entity
public class Employee {
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "info") })
Integer id;
@OneToOne
EmployeeInfo info;
}
11、guid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "guid")
12、uuid.hex
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid.hex")
13、sequence-identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence-identity",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
四、通過@GenericGenerator自定義主鍵生成策略
如果實際應用中,主鍵策略為程式指定了就用程式指定的主鍵(assigned),沒有指定就從sequence中取。
明顯上面所討論的策略都不滿足,只好自己擴充套件了,整合assigned和sequence兩種策略。
Java程式碼
public class AssignedSequenceGenerator extends SequenceGenerator implements
PersistentIdentifierGenerator, Configurable {
private String entityName;
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
entityName = params.getProperty(ENTITY_NAME);
if (entityName==null) {
throw new MappingException("no entity name");
}
super.configure(type, params, dialect);
}
public Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
Serializable id = session.getEntityPersister( entityName, obj )
.getIdentifier( obj, session.getEntityMode() );
if (id==null) {
id = super.generate(session, obj);
}
return id;
}
}
實際應用中,定義同sequence。
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "AssignedSequenceGenerator",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
值得注意的是,定義的這種策略,就像開啟了潘多拉魔盒,非常不可控。正常情況下,不建議這麼做。
策略解釋
“assigned”
主鍵由外部程式負責生成,在 save() 之前指定一個。
“hilo”
通過hi/lo 演算法實現的主鍵生成機制,需要額外的資料庫表或欄位提供高位值來源
“seqhilo”
與hilo 類似,通過hi/lo 演算法實現的主鍵生成機制,需要資料庫中的 Sequence,適用於支援 Sequence 的資料庫,如Oracle。
“increment”
主鍵按數值順序遞增。此方式的實現機制為在當前應用例項中維持一個變數,以儲存著當前的最大值,之後每次需要生成主鍵的時候將此值加1作為主鍵。這種方式可能產生的問題是:不能在叢集下使用。
“identity”
採用資料庫提供的主鍵生成機制。如DB2、SQL Server、MySQL 中的主鍵生成機制。
“sequence”
採用資料庫提供的 sequence 機制生成主鍵。如 Oralce 中的Sequence。
“native”
由 Hibernate 根據使用的資料庫自行判斷採用 identity、hilo、sequence 其中一種作為主鍵生成方式。
“uuid.hex”
由 Hibernate 基於128 位 UUID 演算法 生成16 進位制數值(編碼後以長度32 的字串表示)作為主鍵。
“uuid.string”
與uuid.hex 類似,只是生成的主鍵未進行編碼(長度16),不能應用在 PostgreSQL 資料庫中。
“foreign”
使用另外一個相關聯的物件的識別符號作為主鍵。
在 IBM Bluemix 雲平臺上開發並部署您的下一個應用。
開始您的試用
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 策略生成主鍵
@Entity
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 專案持久層開發中以不變應萬變的姿態來面對紛繁複雜的具體情況了。
一、JPA通用策略生成器
通過annotation來對映hibernate實體的,基於annotation的hibernate主鍵標識為@Id,
其生成規則由@GeneratedValue設定的.這裡的@id和@GeneratedValue都是JPA的標準用法,
JPA提供四種標準用法,由@GeneratedValue的原始碼可以明顯看出.
Java程式碼
@Target({METHOD,FIELD})
@Retention(RUNTIME)
public @interface GeneratedValue{
GenerationType strategy() default AUTO;
String generator() default "";
}
其中GenerationType:
Java程式碼
public enum GenerationType{
TABLE,
SEQUENCE,
IDENTITY,
AUTO
}
JPA提供的四種標準用法為TABLE,SEQUENCE,IDENTITY,AUTO.
TABLE:使用一個特定的資料庫表格來儲存主鍵。
SEQUENCE:根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列。
IDENTITY:主鍵由資料庫自動生成(主要是自動增長型)
AUTO:主鍵由程式控制。
1、TABLE
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
這裡應用表tb_generator,定義為
Sql程式碼
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
插入紀錄,供生成主鍵使用,
Sql程式碼
INSERT INTO tb_generator(id, gen_name, gen_value) VALUES (1,PAYABLEMOENY_PK', 1);
在主鍵生成後,這條紀錄的value值,按allocationSize遞增。
@TableGenerator的定義:
Java程式碼
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
String name();
String table() default "";
String catalog() default "";
String schema() default "";
String pkColumnName() default "";
String valueColumnName() default "";
String pkColumnValue() default "";
int initialValue() default 0;
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
其中屬性說明:
name屬性表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設定的“generator”值中。
table屬性表示表生成策略所持久化的表名,例如,這裡表使用的是資料庫中的“tb_generator”。
catalog屬性和schema具體指定表所在的目錄名或是資料庫名。
pkColumnName屬性的值表示在持久化表中,該主鍵生成策略所對應鍵值的名稱。例如在“tb_generator”中將“gen_name”作為主鍵的鍵值
valueColumnName屬性的值表示在持久化表中,該主鍵當前所生成的值,它的值將會隨著每次建立累加。例如,在“tb_generator”中將“gen_value”作為主鍵的值
pkColumnValue屬性的值表示在持久化表中,該生成策略所對應的主鍵。例如在“tb_generator”表中,將“gen_name”的值為“CUSTOMER_PK”。
initialValue表示主鍵初識值,預設為0。
allocationSize表示每次主鍵值增加的大小,例如設定成1,則表示每次建立新記錄後自動加1,預設為50。
UniqueConstraint與@Table標記中的用法類似。
2、SEQUENCE
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq")
@SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment")
@SequenceGenerator定義
Java程式碼
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerator {
String name();
String sequenceName() default "";
int initialValue() default 0;
int allocationSize() default 50;
}
name屬性表示該表主鍵生成策略的名稱,它被引用在@GeneratedValue中設定的“generator”值中。
sequenceName屬性表示生成策略用到的資料庫序列名稱。
initialValue表示主鍵初識值,預設為0。
allocationSize表示每次主鍵值增加的大小,例如設定成1,則表示每次建立新記錄後自動加1,預設為50。
3、IDENTITY
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
4、AUTO
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
在指定主鍵時,如果不指定主鍵生成策略,預設為AUTO。
Java程式碼
@Id
跟下面的定義是一樣的。
Java程式碼
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
二、hibernate主鍵策略生成器
hibernate提供多種主鍵生成策略,有點是類似於JPA,有的是hibernate特有:
native: 對於 oracle 採用 Sequence 方式,對於MySQL 和 SQL Server 採用identity(自增主鍵生成機制),native就是將主鍵的生成工作交由資料庫完成,hibernate不管(很常用)。
uuid: 採用128位的uuid演算法生成主鍵,uuid被編碼為一個32位16進位制數字的字串。佔用空間大(字串型別)。
hilo: 使用hilo生成策略,要在資料庫中建立一張額外的表,預設表名為hibernate_unique_key,預設欄位為integer型別,名稱是next_hi(比較少用)。
assigned: 在插入資料的時候主鍵由程式處理(很常用),這是 <generator>元素沒有指定時的預設生成策略。等同於JPA中的AUTO。
identity: 使用SQL Server 和 MySQL 的自增欄位,這個方法不能放到 Oracle 中,Oracle 不支援自增欄位,要設定sequence(MySQL 和 SQL Server 中很常用)。
等同於JPA中的INDENTITY。
select: 使用觸發器生成主鍵(主要用於早期的資料庫主鍵生成機制,少用)。
sequence: 呼叫底層資料庫的序列來生成主鍵,要設定序列名,不然hibernate無法找到。
seqhilo: 通過hilo演算法實現,但是主鍵歷史儲存在Sequence中,適用於支援 Sequence 的資料庫,如 Oracle(比較少用)
increment: 插入資料的時候hibernate會給主鍵新增一個自增的主鍵,但是一個hibernate例項就維護一個計數器,所以在多個例項執行的時候不能使用這個方法。
foreign: 使用另外一個相關聯的物件的主鍵。通常和<one-to-one>聯合起來使用。
guid: 採用資料庫底層的guid演算法機制,對應MYSQL的uuid()函式,SQL Server的newid()函式,ORACLE的rawtohex(sys_guid())函式等。
uuid.hex: 看uuid,建議用uuid替換。
sequence-identity: sequence策略的擴充套件,採用立即檢索策略來獲取sequence值,需要JDBC3.0和JDK4以上(含1.4)版本
hibernate提供了多種生成器供選擇,基於Annotation的方式通過@GenericGenerator實現.
hibernate每種主鍵生成策略提供介面org.hibernate.id.IdentifierGenerator的實現類,如果要實現自定義的主鍵生成策略也必須實現此介面.
Java程式碼
public interface IdentifierGenerator {
/**
* The configuration parameter holding the entity name
*/
public static final String ENTITY_NAME = "entity_name";
/**
* Generate a new identifier.
* @param session
* @param object the entity or toplevel collection for which the id is being generated
*
* @return a new identifier
* @throws HibernateException
*/
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException;
}
IdentifierGenerator提供一generate方法,generate方法返回產生的主鍵.
三、@GenericGenerator
自定義主鍵生成策略,由@GenericGenerator實現。
hibernate在JPA的基礎上進行了擴充套件,可以用一下方式引入hibernate獨有的主鍵生成策略,就是通過@GenericGenerator加入的。
比如說,JPA標準用法
Java程式碼
@Id
@GeneratedValue(GenerationType.AUTO)
就可以用hibernate特有以下用法來實現
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
@GenericGenerator的定義:
Java程式碼
@Target({PACKAGE, TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface GenericGenerator {
/**
* unique generator name
*/
String name();
/**
* Generator strategy either a predefined Hibernate
* strategy or a fully qualified class name.
*/
String strategy();
/**
* Optional generator parameters
*/
Parameter[] parameters() default {};
}
name屬性指定生成器名稱。
strategy屬性指定具體生成器的類名。
parameters得到strategy指定的具體生成器所用到的引數。
對於這些hibernate主鍵生成策略和各自的具體生成器之間的關係,在org.hibernate.id.IdentifierGeneratorFactory中指定了,
Java程式碼
static {
GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
}
上面十二種策略,加上native,hibernate一共預設支援十三種生成策略。
1、native
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "native")
2、uuid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid")
3、hilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "hilo")
4、assigned
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "assigned")
5、identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "identity")
6、select
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name="select", strategy="select",
parameters = { @Parameter(name = "key", value = "idstoerung") })
7、sequence
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
8、seqhilo
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "seqhilo",
parameters = { @Parameter(name = "max_lo", value = "5") })
9、increment
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "increment")
10、foreign
Java程式碼
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "employee") })
注意:直接使用@PrimaryKeyJoinColumn 報錯(?)
Java程式碼
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
例如
Java程式碼
@Entity
public class Employee {
@Id Integer id;
@OneToOne @PrimaryKeyJoinColumn
EmployeeInfo info;
...
}
應該為
Java程式碼
@Entity
public class Employee {
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name = "idGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "info") })
Integer id;
@OneToOne
EmployeeInfo info;
...
}
11、guid
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "guid")
12、uuid.hex
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "uuid.hex")
13、sequence-identity
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "sequence-identity",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
四、通過@GenericGenerator自定義主鍵生成策略
如果實際應用中,主鍵策略為程式指定了就用程式指定的主鍵(assigned),沒有指定就從sequence中取。
明顯上面所討論的策略都不滿足,只好自己擴充套件了,整合assigned和sequence兩種策略。
Java程式碼
public class AssignedSequenceGenerator extends SequenceGenerator implements
PersistentIdentifierGenerator, Configurable {
private String entityName;
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
entityName = params.getProperty(ENTITY_NAME);
if (entityName==null) {
throw new MappingException("no entity name");
}
super.configure(type, params, dialect);
}
public Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
Serializable id = session.getEntityPersister( entityName, obj )
.getIdentifier( obj, session.getEntityMode() );
if (id==null) {
id = super.generate(session, obj);
}
return id;
}
}
實際應用中,定義同sequence。
Java程式碼
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "AssignedSequenceGenerator",
parameters = { @Parameter(name = "sequence", value = "seq_payablemoney") })
值得注意的是,定義的這種策略,就像開啟了潘多拉魔盒,非常不可控。正常情況下,不建議這麼做。
相關文章
- hibernate主鍵生成策略
- Hibernate 主鍵的生成策略
- Hibernate框架的主鍵生成策略框架
- Java Hibernate 主鍵生成10大策略Java
- Hibernate主鍵策略
- 透過JPA註解獲取某個類主鍵欄位
- 資料庫主鍵 ID 生成策略資料庫
- Hibernate註解(一)之持久化實體持久化
- Hibernate 自定義主鍵
- java主鍵生成Java
- [轉]eclipse藉助hibernate tool從資料庫逆向生成Hibernate實體類Eclipse資料庫
- 請教Hibernate的多主鍵操作如何實現?
- 分散式主鍵生成分散式
- JPA的Column註解總結
- 在JPA中請優先使用sequence策略生成實體識別符號的值 - Vlad Mihalcea符號
- Kudu主鍵選擇策略
- Hibernate/JPA如何保證不生成多餘的SQL語句?SQL
- Mybatis-Plus3.0預設主鍵策略導致自動生成19位長度主鍵id的坑MyBatisS3
- Hibernate自定義產生主鍵方式
- 操作hibernate多主鍵的問題?
- 如何在Hibernate/JPA的實體和查詢中使用Java 8 Optional?Java
- Hibernate的查詢方式與策略
- JPA和Hibernate的樂觀鎖與悲觀鎖
- 操作hibernate多主鍵的問題?(2)
- hibernate複合主鍵查詢問題
- @Query註解的用法(Spring Data JPA)Spring
- 如何透過Hibernate/JPA的SqlResultSetMapping生成需要資料的DTO?SQLAPP
- 淺談JPA二:聊聊Hibernate
- JPA/Hibernate/Spring Data概念Spring
- Spring boot 四 JPA HibernateSpring Boot
- websphere 6.1如何將open jpa 切換為hibernate jpaWeb
- 主鍵與主鍵索引的關係索引
- Hibernate中@Struct註解型別Struct型別
- JPA的事務註解@Transactional使用總結
- 解鎖你的資料庫:JPA和Hibernate的樂觀鎖與悲觀鎖資料庫
- 使用JPA和Hibernate延遲載入實體屬性的最佳方法 - Vlad Mihalcea
- JPA 實體髒檢查與儲存同步(Dirty & Flush)
- Oracle主鍵與複合主鍵的效能分析Oracle