hibernate(二)一級緩存和三種狀態解析


      序言

        前一篇文章知道了什么是hibernate,並且創建了第一個hibernate工程,今天就來先談談hibernate的一級緩存和它的三種狀態,先要對着兩個有一個深刻的了解,才能對后面我要講解的一對多,一對一、多對多這種映射關系更好的理

 

                                               --WZY

一、一級緩存和快照

    什么是一級緩存呢?

      很簡單,每次hibernate跟數據庫打交道時,都是通過session來對要操作的對象取得關聯,然后在進行操作,那么具體的過程是什么樣的呢?

        1、首先session將一個對象加入自己的管理范圍內,其實也就是把該對象放入自己的一級緩存中,例如,session.save(xxx);這個語句就是將xxx保存在自己的一級緩存中,等待事務提交后,hibernate才真正的發sql語句,對數據庫進行操作。注意:session進行操作的時候,是將對象加入自己的一級緩存,並不是就直接跟數據庫打交道了。

        2、在一級緩存中會做些什么事情呢?為什么能夠知道是發insert、還是update又或者delete呢?那這里就要提到一個快照的概念了,講講內部是什么原理。

舉例來說明問題吧。

session.save()

        User user = new User();
        user.setUsername("xiaoming");
        user.setPassword("123");
        
        session.save(user);//加入了一級緩存,這其中做了什么事情呢?看圖
        user.setUsername("baibai");//那這里改了user的屬性,具體會發生什么事情呢 //提交事務
        tx.commit();
        //關閉session,持久化對象就會變為脫管狀態。
        session.close();

        

 發送的sql語句  

//insert語句
Hibernate:
    insert
    into
        user
        (username, password, c_id)
    values
        (?, ?, ?)
//update語句
Hibernate:
    update
        user
    set
        username=?,
        password=?,
        c_id=?
    where
        id=?

session.get()

        //通過圖中分析,通過select語句查詢,並且user會在session的一級緩存中,
        User user =(User)session.get(User.class, "8");
        //會發出update語句。
        user.setUsername("heihei");

分析圖

      

結果

//select語句
Hibernate: 
    select
        user0_.id as id0_0_,
        user0_.username as username0_0_,
        user0_.password as password0_0_,
        user0_.c_id as c4_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
//update語句
Hibernate: 
    update
        user 
    set
        username=?,
        password=?,
        c_id=? 
    where
        id=?

 

在舉一個例子說明有一級緩存的存在,對於緩存來說,肯定有對緩存的一些操作,比如evict(xx)清除xx緩存、clear():清除一級緩存中所有數據、flush()刷出一級緩存(也就是不用事務提交緩存就刷出來了,就會發sql語句,事務提交也是一個刷出緩存的方法,) 就用上面這個get的例子

session.evict()

        //通過圖中分析,通過select語句查詢,並且user會在session的一級緩存中,
        User user =(User)session.get(User.class, "15");
        //將user移除session一級緩存
        session.evict(user);
        //不會發出update語句。
        user.setUsername("heihei");      

 結果就發送一條select查詢語句

Hibernate: 
    select
        user0_.id as id0_0_,
        user0_.username as username0_0_,
        user0_.password as password0_0_,
        user0_.c_id as c4_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?

 

     總結一下:通過上面的例子和講解,我們應該知道,並不是session一操作對象,就立馬會發sql語句,而是先將其保存到自己內部的一級緩存中,如果不手動刷出緩存,就會等待事務提交時刷出緩存,然后再進行發送sql語句,對數據庫進行操作。並且其中有一個快照區需要知道是干嘛的,理解了快照區,就可以知道每次會發送幾條sql語句。注意一點,session每次操作的是在一級緩存中的對象,不會操作快照區中的對象,快照區只是用來當作比較的一個地方,當對象加入一級緩存之后,外部在怎么改變,也只是改變快照區中的數據,並不是改變一級緩存中對象的屬性。這點就記清楚。如果還不是很清楚,等下面在帶你們看幾個例子,就瞬間明白了。

 

 

 

二、hibernate中對象的三種狀態  

       瞬時狀態transient、持久狀態(托管)persistent、游離(脫管)detached狀態     
          注意:托管、脫管要分清楚,分不清楚就用持久和游離

      瞬時狀態:使用new操作符初始化的對象的狀態就是瞬時的,

          1、在數據庫表中,沒有任何一條數據與它對應

          2、不在session的緩存中

      持久狀態:在session的緩存中,

          1、在session的緩存中(注意,很多書上都覺得持久化狀態都在數據庫表中有相應記錄,這個是錯誤的,比如一個瞬時狀態的對象剛被session.save(),事務還沒提交,此時瞬時狀態就已經變為持久化狀態了,但是在數據庫中還沒有記錄)

          2、在數據庫中可能有記錄。

      脫管狀態:從session的緩存中移除出來了

          1、是從session緩存中出來的,也就是從持久狀態轉變而來的,沒有別的方式能到達游離(脫管)狀態,只有這一種。

          2、不在session的管理范圍內

          3、在數據庫中有記錄

        

      根據上面的文字性描述,可能還不太理解,那現在根據我畫的圖和官方給予的圖來加深印象吧。

 

           誤區1、很多書上覺得通過id值就可以判斷對象是在哪個狀態,比如說,沒有id值,就是瞬時狀態,有id並且在session管理范圍內,就是持久狀態,有id不在session范圍內就是脫管狀態,   

            解釋:從上面的解釋來看,瞬時狀態變為脫管狀態,只要加上id就行了,但是從官方圖來說,瞬時狀態不能直接變為游離狀態,而游離狀態可以通過delete直接到達瞬時狀態(其實說直接,也是先將游離狀態的對象加入到session緩存中變為持久狀態,然后delete,在變為瞬時狀態而已。),那么上面所用的通過有沒有id值來判斷三種狀態就是有偏差的,可以這么理解,瞬時狀態在數據庫中就一定沒有對應的記錄,而游離狀態一定是通過持久狀態轉變而來的,並且在數據庫中可能有記錄(沒有記錄是因為多線程,有別的程序把那條記錄給刪除了,所以一般就覺得是有記錄的),所以瞬時狀態和游離狀態的區分點就在:從什么轉變而來的,如果直接new的,那么就是瞬時狀態對象,如果從持久態轉變的,那么就是游離狀態。

 

             誤區2:很多書上或者博客中會說識別是不是持久化狀態,是看在session緩存內,還有就是在數據庫中有記錄。

             解釋:這里的前半句對,但是后半句錯誤,持久狀態可能在數據庫中有記錄,也可能在數據庫中沒有記錄,說說沒有記錄的時候吧,就是當session.save保存瞬時狀態時,事務還沒有提交,對象還只是在session的一級緩存中,數據庫中就沒有該記錄,但此時它就已經是持久狀態對象了。

    

           誤區3:認為session一操作,就會發出sql語句,這個上面已經說明白了,應該很清楚了。

   

     小總結一下:怎么區分這三種狀態

          1、如果是在session緩存中,那么就一定是持久狀態

          2、如果是剛new出來的對象,那么就肯定是瞬時狀態

          3、如果是從session緩存中出來的,也就是通過一些session.clear、ecivt等操作,清除了緩存的,那么就是游離狀態,

        只有瞬時狀態能確定數據庫中沒有對應記錄,其他兩個狀態,都是不確定數據庫中是否有對應記錄

        一切都以官方給出的圖為准,其他的快速識別狀態的方法,我認為是不可取的,自己還是沒弄明白其中的道理,也就永遠沒把握自己是不是對的。

    

    講了那么久的理論,現在就通過那張官方給的圖,讓我們來實驗試驗各種狀態間的轉換吧。

1、怎么變成瞬時狀態、和如何從瞬時狀態變為持久狀態?   

        User user = new User();//瞬時狀態 user.setUsername("aaa");//瞬時狀態 session.save(user);//持久狀態,這里使用saceOrUpdate也一樣。將user放在session的一級緩存中,快照區也有一份 //提交事務  tx.commit();//事務提交之后,才發送sql語句 //關閉session session.close();//session關閉后,user就變為游離(脫管)狀態了

2、怎么直接變為持久狀態   

        //從數據庫中查詢id為7的用戶,並且此時user在session的一級緩存中,快照區也有一份一樣的 User user = (User) session.get(User.class, "7"); //提交事務  tx.commit(); //關閉session,持久化對象就會變為脫管狀態。 session.close();

 

3、持久狀態變為瞬時狀態

   

        //並且user會在session的一級緩存中,持久狀態,快照區也有一份一樣的
        User user =(User)session.get(User.class, "15");
        //將user移除session一級緩存,並且從數據庫中刪除該記錄,快照區中的對象也刪除了,所以變成瞬時狀態而不是變成游離狀態
        session.delete(user);
        //不會發出update語句。改變的只是瞬時狀態的屬性
        user.setUsername("heihei");

 

4、持久狀態變為游離狀態

 

        //並且user會在session的一級緩存中,持久狀態,快照區中也有一份一樣的
        User user =(User)session.get(User.class, "15");
        //將user移除session一級緩存,並沒有刪除數據庫記錄,所以變為了游離狀態,清除緩存,那么快照區中也沒了
        session.evict(user);
        //不會發出update語句。改變的只是游離狀態的屬性,沒影響
        user.setUsername("heihei");

 

5、游離狀態變為持久狀態

     

        //並且user會在session的一級緩存中,持久狀態,快照區有一份一樣的
        User user =(User)session.get(User.class, "15");
        //將user移除session一級緩存,並沒有刪除數據庫記錄,所以變為了游離狀態。快照區中也沒了
        session.evict(user);
        //不會發出update語句。改變的只是游離狀態的屬性,沒影響
        user.setUsername("heihei");
        //會發出update語句進行更新,重新回到持久狀態。加入到一級緩存中。快照區中也有一份一樣的。
        session.update(user);
     //猜一下這里會不會發兩條update語句?如果覺得是兩條的話,就沒記住我說的話,肯定是一條update語句呀,自己可以看上面講解快照區時的圖片,這里改變了快照區中user的屬性,
     //因為上面用的是update方法,快照區會先跟一級緩存中的對比,如果有不一樣,那么就發送update語句,而不會先發一個update然后再對比,不一樣在發update,跟insert不一樣。
     user.setUsername("sss");

6、游離狀態變為瞬時狀態

     這個也沒什么好講的,上面已經分析過了,游離狀態直接變為瞬時狀態其實也就是先進過持久狀態,然后再變為瞬時狀態。

 

 

 


三、經過上面的學習,現在深入一點,讓你知道會發送多少條sql語句。

     1、Test01

       session = HibernateUtil.openSession();
            session.beginTransaction();
            User user = new User();
            user.setUsername("aaa");
            user.setPassword("aaa");
            user.setBorn(new Date());
            //以上u就是Transient(瞬時狀態),表示沒有被session管理並且數據庫中沒有
            //執行save之后,被session所管理,而且,數據庫中已經存在,此時就是Persistent狀態
            session.save(user);
            //此時u是持久化狀態,已經被session所管理,當在提交時,會把session中的對象和快照區的對象進行比較
            //如果兩個對象中的值不一致就會繼續發出相應的sql語句
            user.setPassword("bbb");
            //此時會發出2條sql,一條用戶做插入,一條用來做更新
            session.getTransaction().commit();

    Hibernate: insert into t_user (born, password, username) values (?, ?, ?)

    Hibernate: update t_user set born=?, password=?, username=? where id=?

    Test02

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            session = HibernateUtil.openSession();
            session.beginTransaction();
            User u = new User();
            u.setBorn(new Date());
            u.setUsername("zhangsan");
            u.setPassword("zhangsan");
            session.save(u);
            u.setPassword("222");
            //該條語句沒有意義
            session.save(u);
            u.setPassword("zhangsan111");
            //沒有意義
            session.update(u);
            u.setBorn(sdf.parse("1988-12-22"));
            //沒有意義
            session.update(u);
            session.getTransaction().commit();

    沒有意義是什么意思呢?記得我一開始說的那個注意點嘛,所有的操作都會先存放在session的一級緩存中,當對象進入一級緩存中,在怎么改變屬性,都只改變快照區中對象的屬性,在用session進行操作,還是操作的一級緩存中的對象,記住了這一點,那么上面的程序就簡單了,u保存到了緩存中,u改變屬性,session在save u沒一點意義,並且連續改改,也只是改快照區中的屬性,等到事務提交的時候,在做對比和進行相應的操作。

    Hibernate: insert into t_user (born, password, username) values (?, ?, ?)

    Hibernate: update t_user set born=?, password=?, username=? where id=?

 

   Test03

       SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            session = HibernateUtil.openSession();
            session.beginTransaction();
            User u = new User();
            u.setId(5);
            //完成update之后也會變成持久化狀態
            session.update(u);
            u.setBorn(sdf.parse("1998-12-22"));
            u.setPassword("lisi");
            u.setUsername("lisi");
            //會拋出異常
            u.setId(333);
            session.getTransaction().commit();

      這個例子會報異常,就是上面我說過的,在一級緩存中的對象,如果改變快照區中的id屬性,就會報異常

 

      在這里,推薦一個博文,如果還想看更多的例子,就進去這個文章里面看,http://www.cnblogs.com/xiaoluo501395377/p/3380270.html 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

        

 

 

 

 

  

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM