背景:java
一個偶然的機會,我作了一個例子,中間我遇到了一個有意思的問題,就是在執行commit方法以前,作了兩次save操做,以下:mysql
這樣執行以後,報了一個:關於session主鍵衝突的異常,這以前,我也見過一個,就是整棵繼承樹映射一張表的時候,主鍵自增加不配置時,這個,我沒有弄啊!而後開始這種查找資料,後來知道,commit方法會隱形的調用一個flush方法,而這個方法不是按照咱們代碼來提交事務的,而是會按着save、update、delete這個順序來提交事務,因此會連續有兩個save變成持久化對象,而形成session生成的Oid重複。web
也就是由於這個,我對flush感了興趣,下面咱們介紹一下關於flush的那些事:sql
一、flush有什麼用?數據庫
調用flush方法,會按save,update,delete順序執行,把緩存中的數據flush入數據庫中,並清空緩存區;這裏注意啊,這裏只是將緩存中的數據同步到數據庫中,這裏並無在數據庫中insert數據,能夠理解爲把緩存內數據同步到一張臨時表內(緩存區),這時候,直接查詢數據庫是沒有新添數據的,可是使用發送sql語句查詢卻能夠查出數據來(前提:數據庫隔離級別要爲 未提交讀),當提交事務時,數據庫事務管理器提交事務執行提交過來的sql語句,修改數據庫數據。也就是說,只有commit纔會更改數據庫數據。緩存
整理一下啊:flush方法的主要做用就是清理緩存,強制數據庫與hibernate緩存同步,以保證數據的一致性。其實在session持久化操做和數據庫中之間還有一層對象緩衝區session
flush的主要動做就是向數據庫發送一系列的sql語句,並執行這些sql語句,可是不會向數據庫提交。而commit方法則會首先調用flush方法,而後提交事務。這就是爲何咱們僅僅調用flush的時候記錄並未插入到數據庫中的緣由,由於只有提交了事務,對數據庫所作的更新纔會被保存下來。由於commit方法隱式的調用了flush,因此通常咱們都不會顯示的調用flush方法。app
二、關於主鍵生成策略的一點隱私:oop
當你的id主鍵生成策略爲native,調用session.save後,將執行insert語句,返回有數據庫生成的id 歸入了session的管理,修改了session中existsInDatabase狀態爲true,若是數據庫的隔離級別設置爲未提交讀,那麼咱們能夠看到save過的數據 ;ui
當id主鍵生成策略採用的是uuid,調用完成save後,只是將user歸入到了session的管理,不會發出insert語句,可是id已經生成,session中existsInDatabase狀態爲false;
三、最後那如何解決上面的問題呢,很簡單,在update以後,執行session.flush,而後再第二個save以後,再執行一次flush就能夠,
這樣就能按照咱們的想法順序緩存sql語句了有木有?
四、總結:
因爲flush()的特殊處理機制,雖然不建議使用此方法,可是在一些複雜的事務處理過程當中,加入此方法雖然會破壞事務的一個提交的完整性,可是能夠規避一些不可預見的異常狀況!