標籤:
前言
每張表都有主鍵,可分別簡單主鍵和組合主鍵,簡單主鍵為表中的一列,組合主鍵為表中的幾列。主鍵的建置原則有許多種,其中,序列是oracle常見的主鍵建置原則之一。本文主要講解JPA映射主鍵的技術細節。其中,主鍵建置原則是oracle序列,JPA實現是Hibernate。
oracle序列建立序列
drop sequence testsequence;Create sequence testsequence Increment by 1 Start with 1 NOMAXVALUE NOMINVALUE Nocycle nocache;
序列testsequence,從1開始遞增,每次遞增1。不指定最大值、最小值、不迴圈、不緩衝。
建立表
create table TEST( id NUMBER not null, name VARCHAR2(30));alter table TEST add constraint PK_ID primary key (ID);create table TEST2( id NUMBER not null, name VARCHAR2(30));alter table TEST2 add constraint PK_TEST2_ID primary key (ID);
建立了兩張表,TEST、TEST2。表結構完全一致,簡單主鍵ID,名字name。
建立觸發器
CREATE OR REPLACE TRIGGER "TG_TEST" BEFORE INSERT ON TEST FOR EACH ROW WHEN (new.id is null) beginselect testsequence.nextval into:new.id from dual;end;/ALTER TRIGGER "TG_TEST" ENABLE;CREATE OR REPLACE TRIGGER "TG_TEST2" BEFORE INSERT ON TEST2 FOR EACH ROW WHEN (new.id is null) beginselect testsequence.nextval into:new.id from dual;end;/ALTER TRIGGER "TG_TEST" ENABLE;
定義兩張表TEST、TEST2的主鍵建置原則是序列testsequence。
JPA主鍵映射註解使用JPA映射主鍵,若主鍵的建置原則是序列的話,需要使用到四個註解,分別是@Id、@Column、@SequenceGenerator、@GeneratedValue。註解@Id表示該實體屬性對應資料庫的主鍵欄位,註解@Column表示該實體屬性對應資料的欄位的名稱,@SequenceGenerator定義一個序列, @GeneratedValue應用一個序列。
private long id; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence") @SequenceGenerator(name="sequence", sequenceName="testSequence", initialValue=1, allocationSize=1) @Column(name = "ID") public long getId() { return this.id; } public void setId(long id) { this.id = id; }
@SequenceGenerator,屬性name,表示序列定義的名稱,屬性sequenceName,表示序列的名稱。這兩個屬性的解釋很像,但並非一回事。屬性name指的是序列定義本身的名稱,屬性sequenceName表示該定義涉及到的序列的名稱。屬性initialValue,表示序列初始值,屬性allocationSize,表示序列遞增或遞減的幅度。@GeneratedValue,屬性strategy,表示主鍵建置原則,GenerationType.SEQUENCE表示主鍵建置原則是序列,屬性generator,表示應用序列定義的名稱。@GeneratedValue的屬性generator和@SequenceGenerator的屬性name保持一致。現在,執行持久化動作。
Test test = new Test(); test.setName("John Wiley & Sons"); manager.persist(test);
查詢資料庫,資料的情況如下:
根據原始碼,註解@SequenceGenerator的屬性initialValue、 allocationSize為可選項,預設值分別是1、50。
/** * (Optional) The value from which the sequence object * is to start generating. */ int initialValue() default 1; /** * (Optional) The amount to increment by when allocating * sequence numbers from the sequence. */ int allocationSize() default 50;
但是,根據測試,結果並非如原始碼展示的那樣。以上序列重建立立、清空表TEST的資料,把@SequenceGenerator的屬性initialValue、 allocationSize去掉,保持預設。執行持久化實體的動作。查詢資料庫,資料的情況如下:
若是再一次重建立立以上序列,清空表TEST的資料,重啟web server,執行實體持久化實體的工作,資料庫的ID並不一定從100開始。也就是說initiaValue的值是隨機的,allocationSize也並非是50。而且,從插入表中的主鍵數值看來,已經脫離了序列的運作。從可以看出,序列testSequence的LAST_NUMBER 是3,但是程式插入的主鍵數值卻是100,101。因此,建議在寫註解@SequenceGenerator時,雖然屬性 initialValue、 allocationSize是可選的,但要明確為這兩個屬性指定數值,並保持和資料庫對序列的定義完全一致。應用範圍根據原始碼,在持久化單元內,序列定義是全域。在一個實體內定義的序列定義,可以應用於持久化單元內的其他實體。
/** * Defines a primary key generator that may be referenced by name when * a generator element is specified for the {@link GeneratedValue} * annotation. A sequence generator may be specified on the entity * class or on the primary key field or property. The scope of the * generator name is global to the persistence unit (across all * generator types). * * <pre> * Example: * * @SequenceGenerator(name="EMP_SEQ", allocationSize=25) * </pre> * * @since Java Persistence 1.0 */
但是,根據測試,在持久化單元內,序列定義只針對定義它的實體有效。在實體TEST內定義了一個序列定義,名稱為sequence。現在在實體TEST2應用這個序列定義。
private long id; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence") @Column(name = "ID") public long getId() { return this.id; } public void setId(long id) { this.id = id; }
啟動web server,報出錯誤Unknown Id.generator: sequence。
private long id; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence") @SequenceGenerator(name="sequence", sequenceName="testSequence", initialValue=1, allocationSize=1) @Column(name = "ID") public long getId() { return this.id; } public void setId(long id) { this.id = id; }
以上代碼,在實體TEST2內,針對序列testSequence,重新定義了序列定義,持久化一切正常。因此,序列定義只針對定義它的實體有效。在生產環境中,不同的表應該使用不同的序列。這樣才能最大限度保證序列產生的數值在特定的表中,其主鍵數值具有連續性。比如說,一個序列產生的數值類似1、2、3、4、5......在該序列同時應用在表TEST,TEST2,有可能數值1、2分配給了TEST,而把3、4、5分配給了TEST2,這樣看起來,表中的主鍵數值就常常沒有連續性。當然,序列只分配給某個表,也不能保證該表的主鍵數值就一定會有連續性。當遇到交易回復時,序列產生的數值同樣沒有插入到表的主鍵,造成主鍵數值的不連續。
Oracle序列和主鍵映射