1. 程式人生 > >hibernate之inverse、cascade----同時儲存主從表資訊

hibernate之inverse、cascade----同時儲存主從表資訊

總結:inverse不讓主表物件生成快照和快取時生成外來鍵那部分資訊,從而達到兩個save語句執行後只會有兩條insert語句而不會有update語句;cascade(級聯)是為了只寫一句save就能同時儲存主從表資訊。

這是inverse部分:

如果我們需要新增一個user和一個dog,而這個dog正好屬於這個user,那麼我們會這麼寫:

@org.junit.Test
	public void test() {
		Session sesission = SessionFactoryUtils.getSessionFactory().getCurrentSession();
		Transaction tx = sesission.beginTransaction();
		User user = new User();
		user.setUsername("xixixi");
		Dog dog = new Dog();
		dog.setDogname("哈士奇");
		user.getDogs().add(dog);
		dog.setDogmaster(user);
		sesission.save(user);//語句1
		sesission.save(dog);//語句2
		tx.commit();
	}

這樣執行的結果是執行兩條insert語句和一條update語句,其中update語句如下:

update dog set dogmaster = ? where dogid = ?

為什麼?因為在執行完語句1之後,馬上會在session中產生一級快取和快照,這時候user中的那個dog還是沒有OID的,然後當執行完語句2之後,馬上返回一個dog,此時的dog已經有了OID,而這個dog和剛才那個一級快取當中的user中的dog是同一物件(但快照中的那個user的內容是不會改變的,那是拷貝的),所以一級快取中的user物件也隨之改變了,然後hibernate再對dog做快取和快照。從上面可以看出,在commit的時候,會檢測到user發生了變化,而dog不受影響,故會執行後面的update語句。

    那為什麼會是上面那種update方式,就只更新一個欄位呢?始終記住,dogs屬性只不過是User物件用來表達外來鍵的一種方式,所以它的資訊也就是象徵著Dog表中dogmaster那個欄位的資訊,所以還有必要去寫update(dog)來更新全欄位嘛?根本不用啊,所以它只要更新Dog表中那條記錄的外來鍵就可以了,因為整個這條記錄的資訊都記錄在dog物件裡裡,所以獲取引數dogmaster 和 dogid自然很容易了。細想後你發現,這個update語句完全沒有存在的必要,因為語句2執行完後Dog表中的記錄已經完整了,此時在一級快取中的dog就是資料庫中的真實記錄,然後你又根據它去做update操作,這不是廢話?

最後解決方法,簡單了,在User.hbm.xml的Set標籤下配置一下inverse屬性為true,讓user在生成快照時放棄生成外來鍵那部分快照即可:

<set name="dogs" table="dog" inverse="true">
    			<key column="dogmaster"></key>
    			<one-to-many class="Dog"/>
    		</set>

這是cascade:

還是上面的例子,我現在想只寫一句save就同時儲存住表、從表資訊,行不行呢?答案是可以! 因為你想嘛,user物件裡有dog,dog物件裡也有user,從理論上來說肯定可以實現,那麼我們這麼寫:

<!-- 就是這個cascade屬性起了作用,值save-update代表儲存更新操作都會級聯執行。 -->
    		<set name="dogs" table="dog"  inverse="true" cascade="save-update">
    			<key column="dogmaster"></key>
    			<one-to-many class="Dog"/>
    		</set>

然後 session.save(user);

再或者這麼寫:

<many-to-one name="dogmaster" column="dogmaster" class="User" cascade="save-update"></many-to-one>

然後 session.save(dog);

友情提醒:當cascade和inverse屬性都配置了的時候,我們可以實現一句save儲存兩張表的記錄,並且儲存時執行的都是insert不會有update語句出來。但是如果你只配cascade沒有inverse,不要以為只有一句話所以不存在物件的快照和一級快取不一致的問題,就妄想它應該不會輸出update,告訴你,照樣輸出,它執行的原理和你寫兩句save其實是一模一樣的!因為雖然你只寫了一句,但是hibernate執行的時候仍然是一條條insert依次執行的,所以insert完主表資訊後,照樣產生帶外來鍵資訊的快照和一級快取,剩下的就不用說了吧,所以記得兩個屬性一起配,順帶截個圖,這是隻配cascade不配inverse的結果:

Hibernate: 
    insert 
    into
        user
        (username, password) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dog
        (dogname, dogcolor, dogage, dogmaster) 
    values
        (?, ?, ?, ?)
Sun Jul 01 10:50:59 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended.
Hibernate: 
    update
        dog 
    set
        dogmaster=? 
    where
        dogid=?