天天看點

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

俗話說,自己寫的代碼,6個月後也是别人的代碼……複習!複習!複習!涉及的知識點總結如下:

one to many 映射關系

多對一單向外鍵關聯(xml/annotation)

一對多單向外鍵關聯(xml/annotation)

懶加載和積極加載

一對多雙向外鍵關聯(xml/annotation)

many to many 映射關系

多對多單向外鍵關聯(xml/annotation)

多對多雙向外鍵關聯(xml/annotation)

set的inverse元素詳解

問題小結

關聯關系的優缺點

  多對一單向外鍵關聯關系

  注意多對一關聯是多方持有一方的引用。看一個例子,去淘寶購物,那麼一個淘寶使用者可以對應多個購物訂單,如圖所示:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  多的一方是orders,持有一方的引用,也就是users,而在users中無需作任何定義,從訂單到使用者的關系是單向多對一關聯。對應資料庫就是:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  還有比如說學生和班級的關系,多個學生可以屬于同一個班級,這就是從學生到班級也是典型的單向多對一關系,看代碼實作:

  

  基于注解的多對一單向外鍵關聯:

  單向多對一關聯中,多方需要持有一方的引用,那麼多方(學生類)需要額外配置,需要對持有的一方引用使用注解@manytoone (cascade={cascadetype.all}, fetch=fetchtype.eager),設定為級聯操作和饑渴的抓取政策,@joincolumn(name="cid"),而一方(教室類)無需做任何多方的定義。

  注意;多方必須保留一個不帶參數的構造器!

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

一方——班級類無需做多餘的定義,下面是多方——學生實體和配置:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

下面測試:先生成資料庫腳本,再進行學生對象的插入

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

反向建立表的資料庫腳本如下:

create table classroom (cid integer not null auto_increment, cname varchar(255), primary key (cid))

create table students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

插入一個學生對象,會自動生成如下語句:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

hibernate: insert into classroom (cname) values (?)

hibernate: insert into students (cid, sname) values (?, ?)

插入成功:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  基于xml配置實作多對一單向外鍵關聯

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  一方(教室類)無需做任何多方的定義。隻需要維護好自己的屬性配置即可。而多方隻需要加上<many-to-one name="" column=“"/>就ok。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  hibernate.cfg.xml裡加上

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

注意:如果沒有設定級聯all,那麼需要在儲存的時候先儲存班級,在儲存學生,否則出錯: object references an unsaved transient instance - save the transient instance before flushing: 

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  小結:使用<many-to-one>元素進行多對一關聯關系配置,name屬性  指定類的屬性名,column屬性 指定庫表字段名,class屬性  指定類屬性類型(加上姓,即包名),not-null屬性 指定屬性是否允許為空,cascade屬性 指定是否級聯儲存和更新:save-update、delete、all、none。

  一對多單向外鍵關聯

  當類與類建立了關聯,程式能很友善的從一個對象導航到另一個或一組與之關聯的對象,有了student對象,就可以通過student對象得到這個學生所屬的班級的資訊——students.getclassroom();,對于班級對象,如果想要得到某個學生的資訊,怎麼辦呢?這時候可以反過來控制,一方控制多方,下面進行一對多單向外鍵關聯。

  簡單說就是和之前多對一相反,之前是多方持有一方的引用,而一對多關聯關系是一方持有多方的集合的引用,注意差別:這裡是持有多方的集合。

  基于注解的配置:

   @onetomany(cascade={cascadetype.all},fetch=fetchtype.lazy),@joincolumn(name=""),除了級聯之外,還要設定一方為懶加載模式。且外鍵還是加在了多方學生表裡,隻不過控制權變了,之前多對一關聯是多方學生持有班級外鍵,控制班級,現在一對多關聯,表裡還是多方學生持有班級一方的外鍵,隻不過控制權交給了班級,讓班級控制學生。不要混淆。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  注意,不論多對一還是一對多,多方都要顯式保留無參構造器。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  執行資料庫腳本,發現一方(主要方)還是和之前多對一的表結構一樣,多方也是如此。

  執行測試,儲存學生,因為現在關系是一方維護,控制多方。肯定儲存主要方——班級(和之前相反,之前多對一儲存的是多方學生對象),但是本質上還是先儲存的學生班級,再自動儲存學生,這點和多對一本質一樣。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  生成的腳本如下:先插入外鍵的班級對象,在執行五個學生的插入操作,最後執行五個更新,為sid=1。。。5的學生,更新cid為2

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  總結:多對一時候,多方設定eager,一方設定lazy,也就是說,如果是多對一,多方控制一方,那麼多方設定積極加載,一方無需多餘配置,反過來,如果是一對多關系,一方控制多方,那麼一方設定懶加載,多方無需多餘配置,但是不論哪種,多方都顯式加上一個不帶參數的構造器。

  一對多裡的懶加載

  記得之前總結,get和load的查詢方式源碼的時候,就總結了一下懶加載load裡的應用,之前說hibernate中,當通路的資料量過大時,用緩存也不太合适, 因為記憶體容量有限 ,為了減少并發量,減少系統資源的消耗,hibernate用懶加載機制來彌補這種缺陷,但是這隻是彌補而不是用了懶加載總體性能就提高了。懶加載也被稱為延遲加載,它在查詢的時候不會立刻通路資料庫,而是傳回代理對象,比如之前總結的load方式查詢,當真正去使用對象的時候才會通路資料庫。除了load查詢預設使用懶加載,現在我們的一對多關聯映射也使用了lazy加載,下面進行實際驗證測試:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  執行之後,debug發現:在沒有使用班級對象的時候,隻有這樣一條sql語句:從classroom表查詢,cid=1的班級,使用使用as賦給列一個别名。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  等執行到for了,才列印這一語句:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  充分說明這是執行的懶加載模式。一方控制多方,一方設定懶加載,如果什麼都不設定,會是什麼情況?經過驗證,發現和顯式設定懶加載效果一樣,也就是說,one-to-many(元素)的懶加載是預設的,這是必須的,是常用的政策。一對多的時候,查詢主對象時預設是懶加載。即:查詢主對象的時候不會把從對象查詢出來,使用從對象的時候才加載從對象。

  如果人為設定為積極加載,則直接全部查詢,@onetomany(cascade={cascadetype.all},fetch=fetchtype.eager),sql語句為如下,進行了外連接配接的查詢。一次性查了主對象和從對象出來。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  基于xml檔案配置:

  一方作為主要方:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  一方是班級,持有多方的集合,如下配置:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  多方是學生,作為從對象,别忘了,保留無參構造器,如下配置:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  一對多雙向外鍵關聯

  其實類似之前的一對一雙向外鍵關聯,也是互相持有對方的引用,故也叫雙向一對多自身關聯。多方持有一方的引用,@manytoone(cascade={cascadetype.all}),@joincolumn(name="")。反過來,一方也持有多方的集合,@onetomany(cascade={cascadetype.all}),@joincolumn(name="")。代碼如下:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  關鍵是一方,也必須持有多方的集合,形成你中有我,我中有你的局面,互相控制。但是還是注意,本質上,資料庫表裡外鍵cid還是加在了學生表——多方的表裡。

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  測試腳本生成。和之前表一樣,隻不過控制權雙方都有了:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

   此時先儲存誰都可以!控制權是雙方都有。

  基于xml配置:

  多方:<many-to-one name="group" column="gid"></many-to-one>,一方:記住,一方是持有集合

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  本例代碼如下:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  小結:在關系模型中,隻存在外鍵參照關系,而且是many方參照one方。

  多對多的關聯關系映射

  現在有一個角色類,和一個特權類,前者儲存了都有哪些人(角色)擁有哪些特權,後者儲存的是一些特權,比如可以做什麼,不可以做什麼等讓哪些人擁有。如圖類關系:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  這就是一個多對多的例子,他們之間在資料庫如何實作的關聯呢?顯然不能互相持有對方主鍵做外鍵,那麼就需要用到一個新的表——映射表作為中間表:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  再舉一個最熟悉的學生的例子,現實中,學生和教師就構成了多對多的關聯關系。一個教師可以教很多學生,同時一個學生可以師從很多老師,拿這個例子說明。

  多對多單向外鍵關聯

  其中一個多方持有另一個多方的集合對象,而且前面也分析了,還需要建立一個中間表,先看兩個實體對象配置

  基于注解的多對多單向外鍵關系配置:

  一個多方持有另一個多方的集合對象,我讓學生持有老師的集合對象引用,同樣的另一個多方——老師不做多餘配置,且多方要顯式設定一個無參構造器,那麼學生需要使用注解@manytomany和@jointable,設定級聯全部cascade=cascadetype.all,代碼如下:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  另一個多方,老師不做多餘配置:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  生成的資料庫腳本如下:

create table students (sid integer not null auto_increment, sname varchar(255), primary key (sid))

create table teachers (tid integer not null auto_increment, tname varchar(255), primary key (tid))

create table teachers_students (sid integer not null, tid integer not null, primary key (sid, tid))

  主要關注中間表,sid和tid都是作為了中間表的聯合主鍵,他們同時也是外鍵:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  下面進行插入資料的測試,因為學生持有老師集合引用,且設定了級聯,故直接儲存學生就ok:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  基于xml的多對多單向外鍵關系配置:

  學生這個多方持有老師的集合,那麼持有對方集合的學生映射檔案配置如下: 

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  老師表配置就簡單了:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  進行測試(删除之前的表,先删除中間表,在删除老師表,最後删除學生表):

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  多對多雙向外鍵關聯

  和之前的類似,是互相持有對方的集合,雙方持有對方的集合對象,其中一方設定@manytomany(mappedby=""),另一方:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  基于注解的配置,看具體代碼:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  關鍵是另一方的配置,前面總結了,雙向關聯不會真的是互相維持,隻能交給一方去維護:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  生成資料庫腳本:(和之前的單向多對多一樣的表結構,關鍵看實體中控制權的變化)

  基于xml的配置:

  其中一方:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  另一方:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  具體代碼:

Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結
Hibernate(6)—— 一對多 和 多對多關聯關系映射(xml和注解)總結

  注意:set元素配置;

  屬性name  指定類的屬性名,table指定多對多關聯關系中間表,cascade 級聯操作屬性:save-update、delete、all、none,一般all就ok,lazy屬性可以指定是否是懶加載。set的子元素key元素——設定本表在中間表的外鍵名稱。

    inverse屬性設定:

  inverse是hibernate中雙向關聯關系中的基本概念,在xml配置裡,用來設定關系由哪一方來維護,inverse=true 表示被控方,false表示主要方,在多對一,一對一關聯關系中,hibernate預設設定多方的inverse=true,即多方為被控方,一方的inverse=false,即一方為主要方。在多對多關系中需要我們自己設定哪一方為被控方即設定inverse=true。上述例子沒有設定,其實需要手動設定的,對應注解裡的mappedby屬性。

  關聯關系的優缺點

  使用關聯關系,就可以直接操作記憶體中的對象,不用每次都查詢資料庫,會提高效率;而且域模型真實反映了客觀世界的關系,但是缺點就是建立複雜的關聯關系會給程式開發帶來麻煩,當修改一個對象時,會牽連其它的對象,這也是為什麼很多人說什麼hibernate不好用……其實就是沒學好,一般人掌握的不紮實,總出錯,後來mybatis橫空出現,大家就去用它了,比起hibernate來,上手非常簡單,使用原生sql……本質不是一個真正的orm架構模式的實作架構。當然mybatis也有它的優點和缺點,以後再總結它。

  一句話:具體要建立對象之間的什麼關聯關系要根據具體的需求。具體使用什麼架構,要聽上司的,哈哈。、

  問題小結

注意在多對一/一對多關系裡:多方必須保留一個不帶參數的構造器!

如果沒有設定級聯all,那麼需要在儲存的時候先儲存班級,在儲存學生,否則出錯: object references an unsaved transient instance - save the transient instance before flushing: 

多對一時候,多方設定eager加載,一對多的時候,一方設定lazy加載

多對多關聯,多方需要保留一個無參構造器。

辛苦的勞動,轉載請注明出處,謝謝……