使用 Hibernate 和 MySQL 需要知道的五件事

https://www.thoughts-on-java.org/5-things-you-need-to-know-when-using-hibernate-with-mysql/
作者:Thorben Janssen
譯者:oopsguy.com

使用 JPA 和 Hibernate 的其一好處是它提供了數(shù)據(jù)庫特定方言和功能抽象闸溃。因此,理論上卓起,你可以實現(xiàn)一個應用程序,將其連接到一個受支持的數(shù)據(jù)庫消返,并且它可以在不用更改任何代碼的情況下運行腹暖。

Hibernate 真的很好。但老實說浓恳,你有沒有想過自己的應用程序真的能與每個支持的數(shù)據(jù)庫完美運行嗎?

Hibernate 幫你做了很多重要的事碗暗。但是奖蔓,如果你希望應用程序能夠穩(wěn)定運行,你仍需要知道要使用哪種數(shù)據(jù)庫讹堤,并相應地調整配置和代碼吆鹤。

在之前的一篇文章中,我討論了“使用 Hibernate 和 PostgreSQL 數(shù)據(jù)庫需要知道的 6 件事情”洲守。今天我想仔細討論 MySQL 數(shù)據(jù)庫疑务。

1、映射:主鍵

處理和創(chuàng)建主鍵是很基礎的內容梗醇,但也是應用程序最重要的一部分知允。

JPA 規(guī)范的 @GeneratedValue 注解允許你定義用于創(chuàng)建唯一主鍵值的策略。你可以選擇 SEQUENCE叙谨、IDENTITY温鸽、TABLE 和 AUTO

一般來說,我建議使用 SEQUENCE 策略涤垫,因為它允許 Hibernate 使用 JDBC 批處理和其他需要延遲執(zhí)行 SQL INSERT 語句的優(yōu)化策略姑尺。

然而,你不能將此策略與 MySQL 數(shù)據(jù)庫一起使用蝠猬。因為它需要一個數(shù)據(jù)庫序列切蟋,恰好 MySQL 不支持此功能。

因此榆芦,你需要在 IDENTITYTABLE 之間進行選擇柄粹。考慮到 TABLE 策略的性能和可擴展性問題匆绣,答案顯而易見驻右。

如果你正在使用 MySQL 數(shù)據(jù)庫,則應始終使用 GenerationType.IDENTITY崎淳。它使用了有自增特性(autoincremented )的數(shù)據(jù)庫列旺入,這是最有效的可用方法。你可以通過使用 @GeneratedValue(strategy = GenerationType.IDENTITY) 注解主鍵屬性來執(zhí)行此操作凯力。

@Entity
public class Author {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
 
    ...
}

2、映射:Hibernate 5 中 GenerationType.AUTO 問題

當你使用 GenerationType.AUTO 時礼华,Hibernate 會根據(jù) Hibernate 方言生成策略咐鹤。如果你需要支持多個數(shù)據(jù)庫,以下一種常用的方法圣絮。

@Entity
public class Author {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
 
    ...
}

在舊版本中祈惶,Hibernate 為 MySQL 數(shù)據(jù)庫選擇 GenerationType.IDENTITY。這是一個不錯的選擇扮匠。如之前所述捧请,這是最有效的方法。

不幸的是此行為在 Hibernate 5 中發(fā)生了改變棒搜,現(xiàn)在使用的是 GenerationType.TABLE疹蛉,它使用數(shù)據(jù)庫表來生成主鍵。這種方法需要大量數(shù)據(jù)庫查詢和悲觀鎖來生成唯一值力麸。

14:35:50,959 DEBUG [org.hibernate.SQL] - select next_val as id_val from hibernate_sequence for update
14:35:50,976 DEBUG [org.hibernate.SQL] - update hibernate_sequence set next_val= ? where next_val=?
14:35:51,097 DEBUG [org.hibernate.SQL] - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

你可以通過定義一個 @GenericGenerator 來避免這一點可款,以下代碼告訴 Hibernate 使用本地策略生成主鍵值。

@Entity
public class Author {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
 
    ...
}

然后克蚂,Hibernate 將使用 MySQL 自增數(shù)據(jù)庫列來生成主鍵值闺鲸。

14:41:34,255 DEBUG [org.hibernate.SQL] - insert into Author (firstName, lastName, version) values (?, ?, ?)
14:41:34,298 DEBUG [org.hibernate.id.IdentifierGeneratorHelper] - Natively generated identity: 1

3、映射:只讀視圖

使用 JPA 與 Hibernate埃叭,你可以以任何數(shù)據(jù)庫表相同的方式來映射視圖摸恍,只要遵循 Hibernate 的命名約定即可。你只需要實現(xiàn)一個使用了 @Entity 注解的類赤屋,該類的屬性為你想要映射的列立镶。

如果視圖只讀壁袄,你應該使用 @Immutable 注解告訴 Hibernate,它將忽略對該實體的所有更改谜慌。

@Entity
@Immutable
public class BookView {
   
  @Id
  @Column(name = "id", updatable = false, nullable = false)
  private Long id;
 
  @Column(name = "version")
  private int version;
  
  @Column
  private String title;
  
  @Column
  @Temporal(TemporalType.DATE)
  private Date publishingDate;
  
  @Column
  private String authors;
   
  ...
   
}

4然想、查詢:MySQL特有的函數(shù)與數(shù)據(jù)類型

作為一個數(shù)據(jù)庫,MySQL 使用一組自定義函數(shù)和數(shù)據(jù)類型來擴展 SQL 標準欣范。其中一例子是 JSON 數(shù)據(jù)類型sysdate 函數(shù)变泄。

JPA 并不支持這些,但由于有 Hibernate 的 MySQL 方言的支持恼琼,因此你可以使用它們妨蛹。

Query q = em.createQuery("SELECT a, sysdate() FROM Author a ");
List<Object[]> results = q.getResultList();

如果你發(fā)現(xiàn)有 Hibernate MySQL 方言不支持的函數(shù)或者數(shù)據(jù)類型,則可以使用一個 AttributeConverter 將數(shù)據(jù)類型轉換為受支持的數(shù)據(jù)類型晴竞,或使用 JPQL 函數(shù)來調用一個 JPQL 查詢中的任意函數(shù)蛙卤。

但請記住,當使用數(shù)據(jù)庫特有函數(shù)或數(shù)據(jù)類型噩死,你可以將應用程序綁定到特定的數(shù)據(jù)庫颤难。但如果需要支持不同的數(shù)據(jù)庫,你將需要更改應用程序的這部分代碼已维。

5行嗤、查詢:存儲過程

許多數(shù)據(jù)庫管理員喜歡在數(shù)據(jù)庫中使用存儲過程來執(zhí)行繁重的數(shù)據(jù)操作。 大多數(shù)情況下垛耳,這種方法比在 Java 代碼中執(zhí)行相同的操作要快得多栅屏。

但是,大多數(shù) Java 開發(fā)人員并不想使用存儲過程堂鲜。當然栈雳,有一個說法是業(yè)務邏輯分布在多個系統(tǒng)上,這使得它更加難以測試和理解缔莲。另一個原因是在 JPA 2.1 之前哥纫,該規(guī)范并沒有直接支持存儲過程調用。你必須使用原生查詢痴奏,總之感覺很復雜磺箕。

知道 JPA 2.1 引入了 StoredProcedureQuery@NamedStoredProcedureQuery 才有所改變。

@NamedStoredProcedureQuery

@NamedStoredProcedureQuery 注解允許你定義存儲過程調用抛虫,且可通過其名稱在業(yè)務代碼中引用它松靡。以下代碼片段展示了一個簡單示例,它定義了存儲過程 calculate 的調用建椰,附帶了輸入參數(shù) xy 以及輸出參數(shù) sum雕欺。

@NamedStoredProcedureQuery(
    name = "calculate", 
    procedureName = "calculate", 
    parameters = {  @StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "x"),
            @StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "y"),
            @StoredProcedureParameter(mode = ParameterMode.OUT, type = Double.class, name = "sum") })

之后,你可以將 @NamedStoredProcedureQuery 的名稱提供給 EntityManagercreateNamedStoredProcedureQuery,以實例化一個新的 StoredProcedureQuery屠列。

StoredProcedureQuery query = em.createNamedStoredProcedureQuery("calculate");
query.setParameter("x", 1.23d);
query.setParameter("y", 4d);
query.execute();
Double sum = (Double) query.getOutputParameterValue("sum");

在代碼段中可以看到啦逆,你可以設置輸入參數(shù)的值,設置方式與設置 JPQL 查詢的綁定參數(shù)值一樣笛洛。你只需使用輸入參數(shù)的名稱和值來調用 StoredProcedureQuerysetParameter 方法即可夏志。

StoredProcedureQuery

存儲過程調用的編程定義非常類似于前面示例中給出的基于注解的方法。你只需將要執(zhí)行的存儲過程的名稱配合 EntityManager 來調用 createStoredProcedureQuery 即可苛让。還有一個 StoredProcedureQuery 接口沟蔑,你可以使用它來注冊存儲過程的輸入參數(shù)和輸出參數(shù)。

StoredProcedureQuery query = em.createStoredProcedureQuery("calculate");
query.registerStoredProcedureParameter("x", Double.class, ParameterMode.IN);
query.registerStoredProcedureParameter("y", Double.class, ParameterMode.IN);
query.registerStoredProcedureParameter("sum", Double.class, ParameterMode.OUT);

這就是定義存儲過程調用全部需要做的事狱杰。之后瘦材,你可以與 @NamedStoredProcedureQuery 一樣使用它。在執(zhí)行存儲過程調用之前仿畸,需要設置輸入參數(shù)值食棕。

query.setParameter("x", 1.23d);
query.setParameter("y", 4d);
query.execute();

6、總結

Hibernate 已經支持大多數(shù) MySQL 特有的特性错沽。但是簿晓,如果要創(chuàng)建一個輕便且性能良好的應用程序,你還需要記住以上事項千埃。

特別是 Hibernate 5 中生成唯一主鍵值和 GenerationType.AUTO 行為的改變憔儿,可能會在將應用程序部署到生產環(huán)境時產生意外的可擴展性問題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末镰禾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子唱逢,更是在濱河造成了極大的恐慌吴侦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坞古,死亡現(xiàn)場離奇詭異备韧,居然都是意外死亡,警方通過查閱死者的電腦和手機痪枫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門织堂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奶陈,你說我怎么就攤上這事易阳。” “怎么了吃粒?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵潦俺,是天一觀的道長。 經常有香客問我,道長事示,這世上最難降的妖魔是什么早像? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮肖爵,結果婚禮上卢鹦,老公的妹妹穿的比我還像新娘。我一直安慰自己劝堪,他們只是感情好冀自,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幅聘,像睡著了一般凡纳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帝蒿,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天荐糜,我揣著相機與錄音,去河邊找鬼葛超。 笑死暴氏,一個胖子當著我的面吹牛,可吹牛的內容都是我干的绣张。 我是一名探鬼主播答渔,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侥涵!你這毒婦竟也來了沼撕?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤芜飘,失蹤者是張志新(化名)和其女友劉穎务豺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗦明,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡笼沥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娶牌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奔浅。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖诗良,靈堂內的尸體忽然破棺而出汹桦,到底是詐尸還是另有隱情,我是刑警寧澤鉴裹,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布营勤,位于F島的核電站灵嫌,受9級特大地震影響,放射性物質發(fā)生泄漏葛作。R本人自食惡果不足惜寿羞,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赂蠢。 院中可真熱鬧绪穆,春花似錦、人聲如沸虱岂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽第岖。三九已至难菌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔑滓,已是汗流浹背郊酒。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留键袱,地道東北人燎窘。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蹄咖,于是被迫代替她去往敵國和親褐健。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容