一級緩存:
又稱為session緩存,它和session生命周期相同,周期非常短.是事務級別的緩存:
還是以Book和Category這兩個表為例,我們用代碼觀察一個緩存的存在:
假設現在我要去查詢id=1的Book信息:
List<Book> list =(List) session.createQuery("from Book").list(); System.out.println(list.get(0).getName()); Book book = (Book)session.get(Book.class, 1); System.out.println(book.getName());
我這里先查詢了所有的Book,打印出了index=0,即id=1的那本書的書名,接着用get方法再次獲取了id=1的Book,我們看一下控制台的打印信息:
通過打印信息我們能發現一個現象,當第二次我們使用get(..)方法去查詢時,我們如願得到了書名,但卻沒有select語句,說明了什么問題?說明了使用get()方法獲取id=1的Book時,hibernate並沒有去訪問數據庫,而是在某一個地方就得到了這個id=1的Book的信息,不難發現,我們第一次使用createQuery()時,其實已經得到了這條Book的信息,而hibernate將這些信息放到了一個緩存里,當執行查詢語句時,hibernate沒有着急的立即訪問數據庫去查詢,而是先到這個緩存里去找找有沒有他所要查詢的數據,如果有的話,那就皆大歡喜了,不需要訪問數據庫,提高了效率,而這個緩存.就是我們所說的一級緩存,也叫session緩存.
同時我們也能知道,get方法使用了一級緩存,用get查詢數據時,首先檢查緩存中是否有該數據,如果有,直接從緩存中獲取該數據直接返回,如果沒有,再去訪問數據庫查找.load也支持一級緩存,但是同時load同時也支持延遲加載.要注意.
接下繼續測試代碼:
List<Book> list =(List) session.createQuery("from Book").list(); System.out.println(list.get(0).getName()); list =(List) session.createQuery("from Book").list(); System.out.println(list.get(0).getName())
在代碼中,我們執行了兩次session.createQuery(),如果只打印一條sql語句,說明list查詢也支持一級緩存,打印結果是這樣:
很不幸,控制台打印出了兩條sql語句,這就說明了:
list查詢不支持一級緩存,但list查詢會把返回的結果保存到session緩存,同理uniqueResult()查詢也是如此.
接下來繼續測試:
List<Book> list =(List) session.createQuery("from Book").list(); System.out.println(list.get(0).getName()); Iterator<Book> iter = session.createQuery("from Book").iterate(); while(iter.hasNext()){ System.out.println(iter.next().getName()); }
這里我們第一次使用list查詢得到所有Book信息,然后用iterate查詢,得到一個包含所有Book的迭代器集合,那么他會不會支持一級緩存,這是控制台打印的信息:
乍一看,第二次查詢的時候依然打出了sql語句,看來iterate查詢是不會先去session中查找的了,但是仔細觀察第一條sql語句,可以發現,它僅僅查詢了Book的id,但依然打印出了每本書的書名.說明了,iterate依然是在緩存中查詢的數據,所以,iterate是支持一級緩存的,同樣它執行的查詢,也會把返回結果保存到session緩存中.
` 現在再來看一下管理session的幾個方法.
- session.flush():一般是結合事務對象Transaction用的,用來清理緩存,執行sql,避免內存溢出,假設你要保存十萬條數據,你肯定不希望當保存到九萬條的時候程序出錯,然后事務回滾,導致一條數據都沒有保存上,這時候你可以每保存一千條數據就執行一次session.flush(),這樣的話,每保存一千條數據,就會執行到數據庫,然后清理一下session緩存.
- session.evict(...);從session緩存中干掉你指定的某一個對象.
- session.clear():將當前session緩存中保存的所有對象統統干掉.
下面用代碼演示一下evict,和clear方法:
Book book =(Book) session.get(Book.class,1); System.out.println(book.getName()); session.evict(book); //b表示干掉session中保存的所有對象,當然也包括Book對象 //session.clear()
book =(Book) session.get(Book.class,1); System.out.println(book.getName());
按照我們的設想,由於執行第二次查詢之前,我們通過session.evict(book)方法,干掉了book對象,再次查詢時會去訪問數據庫,來看一下控制台打印結果是不是這樣的:
果然如此,跟我們料想的一樣.