在上一篇文章裡,我們從端方向一端建立關聯關系,完成了從文章到作者的關聯關系建立,但在實際的部落格網站中,使用者肯定還需要擷取自己所寫的文章,這時可以建立使用者(一)對文章(多)的單向關聯映射。
先來看我們的一方配置執行個體
下面是我們對應的多方配置
根據這些配置,我們來編寫測試方法:
執行測試方法,我們會看到控制台列印下列sql語句: hibernate: insert into t_user1 (name) values (?) hibernate: insert into t_article1 (content) values (?) hibernate: insert into t_user1_t_article1 (t_user1_id, articles_id) values (?, ?) 在前四句,我們看到在儲存user對象時,級聯儲存了我們的文章對象,最後面三條資訊又是什麼?原來在我們沒有設定@joincolumn(具體使用方法請參考我的上篇文章)。那麼在一對多的關聯配置中,hibernate會預設幫我們生成中間表來完成兩者的映射關系,查詢資料庫,我們會發現 mysql> select * from t_user1_t_article1; +————+————-+ | t_user1_id | articles_id | | 1 | 1 | | 1 | 2 | | 1 | 3 | 3 rows in set (0.00 sec) 确實是通過中間表,将使用者和文章關聯起來了。 這時進行級聯删除測試: “`java user user = (user) session.get(user.class, 1); session.delete(user);
修改對應表名,讓hibernate重新在資料庫中生成表,此時再運作我們的測試方法,會看到:
hibernate: insert into t_user2 (name) values (?)
hibernate: insert into t_article2 (content) values (?)
hibernate: insert into t_article2 (content) values
hibernate: update t_article2 set user_id=? where id=?
此時我們會看到,最後三行換成了更新我們的表屬性值。從這裡我們看出為了更新aritlce表中user_id對應值,我們額外使用多了三條資料,這是很不值的,會額外消耗資料庫的性能,有沒方法使得在插入文章表的同時插入user_id的值呢?檢視hibernate源碼,我們會發現這是因為user作為主動方,它處理關聯對象時必須通過update來完成,如果我們想取消update,應該将user放棄主動,讓另一方(多方)去維護,這又涉及到我們的一對多、多對一雙向關聯了,我們在下一篇文章再具體解決這一問題。
這個時候我們來測試級聯删除:
會得到如下列印資訊: hibernate: update t_article2 set user_id=null where user_id=? hibernate: delete from t_article2 where id=? hibernate: delete from t_user2 where id=? 注意到,它的删除順序是:清除使用者表和文章表的關聯關系(這又是因為使用者表作為主動方,它必須通過此方法來維護關聯關系->然後清除多方文章資訊->最後才删除我們的一方使用者
上面我們基本完成了我們的測試工作,下面我們對配置屬性加以分析:
1. 相對于上一篇我們提到的manytoone屬性,onetomany獨有的屬性有:mapperby和orphanremoval,mpperby是指放棄維護級聯關系,具體我們在雙向關聯中再詳細分析,這裡比較獨特的屬性是orphanremoval
表面意思是去除孤兒,當一方不再關聯多方某一實體a時,自動從資料庫中删除a。下面來看執行個體測試,假如我們先将其設為false
先看看我們資料庫的初始記錄資訊:
+—-+———–+ | id | name | | 2 | oneobject | 1 row in set (0.00 sec) mysql> select * from t_article2; +—-+————–+———+ | id | content | user_id | | 6 | morecontent0 | 2 | | 5 | morecontent1 | 2 | | 4 | morecontent2 | 2 |
接着開始我們的測試:
運作測試代碼,我們會看到控制台僅輸出一條sql語句:
hibernate: update t_article2 set user_id=null where user_id=? and id=?
即
查詢資料庫,此時變成:
| 4 | morecontent2 | null |
現在我們先恢複測試前的資料:
<code>mysql> update t_article2 set user_id = 2 where id = 4;</code>
然後再将對應注解裡的orphanremoval設為true
再次運作我們的測試代碼,控制台輸出:
我們的文章記錄就對應被删除了,這就是去除“孤兒”的含義,在實際開發中,正如我們的身份證總是從屬于某個人的,如果失去這種從屬關系,身份證就沒有意義而可以去除了。
在預設不使用@joincolumn時,多對一關聯中hibernate會為我們自動生成中間表,但如果我們像自己來配置中間表,就可以使用@jointable注解。它的執行個體配置如下:
其中:
1. name為建立的中間表名稱。
2. inversejoincolumns指向對方的表,在這裡指多方的表。
3. joincolumns指向自己的表,即一方的表,這些指向都是通過主鍵映射來完成的。
運作我們的測試代碼:
會發現我們的使用者、文章對應關系都在中間表中建立起來了:
mysql> select * from t_user_articles; +———+————+ | user_id | article_id | | 1 | 1 | | 1 | 2 | | 1 | 3 | 使用中間表的好處是對原來兩張表的結構不對造成任何影響。尤其是在一些老項目中我們可以不修改既定的表結構(事實上在一個項目古老龐大到一定程度就很難去改)、以不侵入原來表的方式建構出一種更清淅更易管理的關系。當然缺點是我們的我們需要維護多一張表,一旦中間表多了,維護起來會愈加麻煩。但綜合來看,我們顯然更推薦用中間表的方式來完成配置。
在我們開始配置一對多的一方時,我們通過set來和多方建立關系,其中提到的一點是可以防止多方相同對象出現。這個相同對應我們資料庫中就是某些屬性列相同,比如:對于article,如果id和content在兩條記錄中都一樣,我們就可以認為兩條記錄是一緻的,是以會自動去重那麼我們來判斷它們的重複關系呢?這個時候就要通過重寫hashcode和equals方法了。
示例如下:
一般來說,我們重寫equal方法就能判斷兩個對象是否相等了,為什麼還要重寫hashcode方法呢?主要是考慮到效率的問題,對于equals方法,當比較規則比較複雜的話就會比較耗時了,而hashcode為每一個對象生成一個散列碼(通過一種神秘的算法,一般為關鍵屬性乘以一個質數),避免了比較慢的運算。不過我們不能因為快就單憑hash碼來判斷兩個對象是否相等,因為hashcode并不能保證能為每一個不同的對象生成唯一的散列碼,是以可能會有兩個hash碼相同,但對象确實不一緻的情況。不過我們知道的是如果連hash碼都不一緻,那兩個對象肯定是不一緻的。根據此思路,我們可以很好地了解在java内部,是如何判斷兩個對象是否相等的:
