【Spring JPA總結(jié)】@GeneratedValue注解介紹

在使用Spring Jpa的時候,除了可以使用Repository提供的一系列自定義的方法(如findBy*)之外,在Entity層使用了很多Java Persistence API相關(guān)的注解。比如熟知的@Entity, @Table, @GeneratedValue等。

關(guān)于Java Persistence API塑崖,官網(wǎng):https://jakarta.ee/specifications/persistence/

本文嘗試解答@GeneratedValue用法文黎,以及與@SequenceGenerator奏路、@GenericGenerator的區(qū)別。

文章內(nèi)容


1. @GeneratedValue介紹

基于Java persistence API v2.2版本的在線Java文檔:https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/generatedvalue
@GeneratedValue注解的主要作用是:聲明主鍵的生成策略臊诊。很自然的鸽粉,它需要和@Id結(jié)合使用。
這個注解有兩個參數(shù):

1. strategy:
  • TABLE:使用一個特定的數(shù)據(jù)庫表格存放主鍵抓艳。
  • SEQUENCE:根據(jù)底層數(shù)據(jù)庫的序列來生成主鍵触机,條件是數(shù)據(jù)庫支持序列。(Oracle)
  • IDENTITY:主鍵有數(shù)據(jù)庫自動生成(主要是自動增長類型)玷或。(MySQL)
  • AUTO:主鍵由程序控制儡首。(默認)
2. generator:

主鍵生成器的名稱,與@SequenceGenerator偏友、@GenericGenerator等注解結(jié)合使用蔬胯。


2. 示例

2.1 示例-1:MySQL - strategy=IDENTITY
主鍵設(shè)置自增長
@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    ...
}

這時候在測試的時候,就算set了一個id位他,也沒有用氛濒,存入的時候還是會算數(shù)據(jù)庫自增的id來产场。
MySQL主健設(shè)置的是自增長,如果在實體類里不加@GeneratedValue舞竿,在save的時候京景,id也能自動存入。但有個問題骗奖,就是這時候不小心在程序里set了這個實體的id确徙,那么就不會自動增長了,就會直接將set的這個id存進去了执桌。

總結(jié)鄙皇,如果使用的是自增長,那么還是要聲明strategy = GenerationType.IDENTITY比較好仰挣。

2.2 示例-2:Oracle - strategy=SEQUENCE

首先在Oracle中定義sequence:
參考:ORACLE SEQUENCE 詳解

create sequence COURSE_SEQ;

然后結(jié)合使用@SequenceGenerator注解和@GeneratedValuegenerator育苟。

關(guān)于@SequenceGenerator的在線文檔:
https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/sequencegenerator,它包含很多屬性椎木,其中示例有用到的:

  • name:自定義的名字违柏,主要是為了另一個注解@GeneratedValuegenerator使用。
  • sequenceName香椎,Oracle表中定義的sequence名字漱竖。
  • allocationSize - 每次主鍵值增加的大小,例如設(shè)置成1畜伐,則表示每次創(chuàng)建新記錄后自動加1馍惹,默認為50.
@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @SequenceGenerator(name = "courseSeq", sequenceName = "COURSE_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "courseSeq")
    private long id;
    ...
}
2.3 示例-3: MySQL - strategy=AUTO
主鍵不自增

strategy=AUTO的意思是主鍵由程序控制。默認策略就是AUTO玛界,所以在Entity中可以不用定義@GeneratedValue注解万矾,也是可以工作的。

@Entity
@Table(name = "COURSE")
public class Course {

    @Id
    private long id;
    ...
}

測試:
我利用Hazelcast來生成分布式ID(當然用Redis或是UUID都可以):

@SpringBootTest
public class CourseRepositoryTest {

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private HazelcastInstance hazelcastInstance;

    @Test
    public void test() {
        Course course = new Course();
        course.setId(hazelcastInstance.getFlakeIdGenerator("courseId").newId());
        course.setName("Test");
        course.setStatus(StatusEnum.Active);
        courseRepository.save(course);
    }
}

運行兩遍慎框,結(jié)果:
插入了兩條

可以看到ID是set了什么良狈,它就save什么。

2.4 示例-4: 結(jié)合使用@GenericGenerator與@GeneratedValue的generator

第2.3的示例笨枯,每次id需要手動set薪丁,感覺也不是很方便,改進的辦法是結(jié)合使用@GenericGenerator@GeneratedValuegenerator馅精。

  • 首先是需要定義@GenericGenerator注解严嗜,name可以隨便取,strategy是ID的實現(xiàn)類洲敢,下面有介紹漫玄。
  • 其次還是需要@GeneratedValue,這時候可以不用定義strategy了(默認即AUTO)压彭,需要定義一個generator睦优,即上面的name渗常。

注:@GenericGenerator注解不在Java Persistence API中,而是hibernate包中的注解刨秆。它的主要作用是用來指定一個ID的生成器的自定義類凳谦。

以下是示例:

@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GenericGenerator(name = "testGenerator", strategy = "com.util.IdGenerator")
    @GeneratedValue(generator = "testGenerator")
    private long id;
    ...
}

Generator實現(xiàn)類:需要實現(xiàn)IdentifierGenerator的generate方法忆畅。我依舊選擇Hazelcast來生成分布式ID:

public class IdGenerator implements IdentifierGenerator {

    @Override
    public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
        HazelcastInstance hazelcastInstance = ApplicationProvider.getBean(HazelcastInstance.class);
        return hazelcastInstance.getFlakeIdGenerator("courseId").newId();
    }
}

測試衡未,可以看到這時候就不需要set id了。程序會自動調(diào)用IdGenerator的generate方法在save前把id給設(shè)置上家凯。測試結(jié)果跟#2.3類似缓醋。

    @Test
    public void test() {
        Course course = new Course();
        course.setName("Test");
        course.setStatus(StatusEnum.Active);
        courseRepository.save(course);
    }
2.5 示例-5,@GenericGenerator已經(jīng)存在的內(nèi)置類

根據(jù)文章Difference between @GeneratedValue and @GenericGenerator
的介紹绊诲,Hibernate在類DefaultIdentifierGeneratorFactory預(yù)先內(nèi)置了很多生成ID的類:

public DefaultIdentifierGeneratorFactory() {
        register( "uuid2", UUIDGenerator.class );
        register( "guid", GUIDGenerator.class );            // can be done with UUIDGenerator + strategy
        register( "uuid", UUIDHexGenerator.class );         // "deprecated" for new use
        register( "uuid.hex", UUIDHexGenerator.class );     // uuid.hex is deprecated
        register( "assigned", Assigned.class );
        register( "identity", IdentityGenerator.class );
        register( "select", SelectGenerator.class );
        register( "sequence", SequenceStyleGenerator.class );
        register( "seqhilo", SequenceHiLoGenerator.class );
        register( "increment", IncrementGenerator.class );
        register( "foreign", ForeignGenerator.class );
        register( "sequence-identity", SequenceIdentityGenerator.class );
        register( "enhanced-sequence", SequenceStyleGenerator.class );
        register( "enhanced-table", TableGenerator.class );
    }

比如使用uuid2來生成ID送粱,這時候@GenericGenerator的strategy值不是一個類名了,而是hibernate預(yù)置的值generator name了掂之。

以下是示例:

@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GeneratedValue(generator = "testGenerator")
    @GenericGenerator(name = "testGenerator", strategy = "uuid2")
    private String id;

注:如果使用strategy=“increment”這類hibernate自帶的自增器抗俄,它是單機版的,并不是分布式的世舰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末动雹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子跟压,更是在濱河造成了極大的恐慌胰蝠,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件震蒋,死亡現(xiàn)場離奇詭異茸塞,居然都是意外死亡,警方通過查閱死者的電腦和手機查剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門钾虐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笋庄,你說我怎么就攤上這事禾唁。” “怎么了无切?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵荡短,是天一觀的道長。 經(jīng)常有香客問我哆键,道長掘托,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任籍嘹,我火速辦了婚禮闪盔,結(jié)果婚禮上弯院,老公的妹妹穿的比我還像新娘。我一直安慰自己泪掀,他們只是感情好听绳,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著异赫,像睡著了一般椅挣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塔拳,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天鼠证,我揣著相機與錄音,去河邊找鬼靠抑。 笑死量九,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的颂碧。 我是一名探鬼主播荠列,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼载城!你這毒婦竟也來了肌似?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤个曙,失蹤者是張志新(化名)和其女友劉穎锈嫩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垦搬,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡呼寸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了猴贰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片对雪。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖米绕,靈堂內(nèi)的尸體忽然破棺而出瑟捣,到底是詐尸還是另有隱情,我是刑警寧澤栅干,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布迈套,位于F島的核電站,受9級特大地震影響碱鳞,放射性物質(zhì)發(fā)生泄漏桑李。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贵白。 院中可真熱鬧率拒,春花似錦、人聲如沸禁荒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呛伴。三九已至勃痴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磷蜀,已是汗流浹背召耘。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工百炬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐隆,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓剖踊,卻偏偏與公主長得像庶弃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子德澈,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容