序言
前一篇文章知道了什麼是hibernate,並且建立了第一個hibernate工程,今天就來先談談hibernate的一級快取和它的三種狀態,先要對著兩個有一個深刻的瞭解,才能對後面我要講解的一對多,一對一、多對多這種對映關係更好的理
--WH
一、一級快取和快照
什麼是一級快取呢?
很簡單,每次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