Spring Boot之Spring Data JPA自定義ID策略

如何指定id策略

在JPA中,我們是通過@id@GeneratedValue來指定id主鍵和id策略的,比如:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private String id;

這樣也就指定了id和生成id所使用的策略,下面我們來看一下都有哪些策略呢

4種JPA策略用法

我們點進@GeneratedValue源碼裏可以看到,strategy屬性是由GenerationType指定的,我們點進GenerationType裏面可以看到這裏定義了四種策略:
- TABLE:使用一個特定的數據庫表格來保存主鍵。
- SEQUENCE:根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列。
- IDENTITY:主鍵由數據庫自動生成(主要是自動增長型)
- AUTO:主鍵由程序控制(也是默認的,在指定主鍵時,如果不指定主鍵生成策略,默認爲AUTO)
這些策略也不是所有數據庫都支持的,支持情況如下表:

策略\數據庫 mysql oracle postgreSQL kingbase
TABLE 支持 支持 支持 支持
SEQUENCE 不支持 支持 支持 支持
IDENTITY 支持 不支持 支持 支持
AUTO 支持 支持 支持 支持

Hibernate拓展id策略

當然,很多時候,這麼幾種策略並不夠用,這裏hibernate也拓展了JPA的id策略,我們可以在org.hibernate.id.IdentifierGeneratorFactory中看到,主要提供了這麼些策略:
1. native: 對於oracle採用Sequence方式,對於MySQL和SQL Server採用identity(自增主鍵生成機制),native就是將主鍵的生成工作交由數據庫完成,hibernate不管(很常用)。
2. uuid: 採用128位的uuid算法生成主鍵,uuid被編碼爲一個32位16進制數字的字符串。佔用空間大(字符串類型)。
3. hilo: 使用hilo生成策略,要在數據庫中建立一張額外的表,默認表名爲hibernate_unique_key,默認字段爲Integer類型,名稱是next_hi(比較少用)。
4. assigned: 在插入數據的時候主鍵由程序處理(很常用),這是generator元素沒有指定時的默認生成策略。等同於JPA中的AUTO。
5. identity: 使用SQL Server和MySQL的自增字段,這個方法不能放到Oracle中,Oracle不支持自增字段,要設定sequence(MySQL和SQL Server中很常用)。等同於JPA中的IDENTITY。
6. select: 使用觸發器生成主鍵(主要用於早期的數據庫主鍵生成機制,少用)。
7. sequence: 調用底層數據庫的序列來生成主鍵,要設定序列名,不然hibernate無法找到。
8. seqhilo: 通過hilo算法實現,但是主鍵歷史保存在Sequence中,適用於支持Sequence的數據庫,如Oracle(比較少用)。
9. increment: 插入數據的時候hibernate會給主鍵添加一個自增的主鍵,但是一個hibernate實例就維護一個計數器,所以在多個實例運行的時候不能使用這個方法。
10. foreign: 使用另外一個相關聯的對象的主鍵。通常和聯合起來使用。
11. guid: 採用數據庫底層的guid算法機制,對應MYSQL的uuid()函數,SQL Server的newid()函數,ORACLE的rawtohex(sys_guid())函數等。
12. uuid.hex: 看uuid,建議用uuid替換。
13. sequence-identity: sequence策略的擴展,採用立即檢索策略來獲取sequence值,需要JDBC3.0和JDK4以上(含1.4)版本 。
具體使用就是多了一個@GenericGenerator註解,指定策略,指定自定義名稱,然後在@GeneratedValue中使用該策略,比如:

@Id
@GeneratedValue(generator  = "myIdStrategy")
@GenericGenerator(name = "myIdStrategy", strategy = "uuid")
@Column(name = "id")
private String id;

其他的類似,就不再多舉例了

到這裏已經有很多策略供我們使用了,但是呢,有時候比如分佈式系統中要求全局id唯一,或者其他一些場景,要求我們有自己的策略,那麼該怎麼做呢?

使用的id策略(以snowflake爲例)

在看其他策略源碼的時候,我們發現他們實現了這樣一個接口IdentifierGenerator,他位於org.hibernate.id包下,我們進入該類,就可以看到源碼註釋上寫着用戶實現此接口可以實現自定義id策略
那麼就很簡單了,我們實現一下這個接口:

public class SnowflakeId implements IdentifierGenerator{
    .
    .
    .
    @Override
    public Serializable generate(SessionImplementor s, Object obj) {
        return getId() + "";
    }
}

實現generate方法,方法體調用我們生成id的方法就好了,這裏省略了生成過程,有需要可以去我代碼裏找一下。

注意IdentifierGenerator接口裏面還寫了一句註釋,必須要實現一個默認的無參構造。當時實現的就少看了這一句,折騰了好久(手動捂臉)。所以大致是這樣的:

public class SnowflakeId implements IdentifierGenerator{

    public SnowflakeId() {
    }
    .
    .
    .

    @Override
    public Serializable generate(SessionImplementor s, Object obj) {
        return getId() + "";
    }
}

自定義完了之後,我們只需要在指定策略的時候使用我們自定義的就好了,@GenericGenerator註解的strategy屬性上說了,使用非默認策略的時候,需要使用全類名,即:

@Id
@GeneratedValue(generator = "snowFlakeId")
@GenericGenerator(name = "snowFlakeId", strategy = "top.felixu.idworker.SnowflakeId")
@Column(name = "id")
private String id;

這樣我們就實現了自己的id策略了

示例代碼

破代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章