hibernate緩存機制與N+1問題

在項目中遇到的趣事

本文基于hibernate緩存機制與N+1問題展開思考,

先介紹何為N+1問題

再hibernate中用list()獲得對象:

 1 /**
 2              * 此時會發出一條sql,將30個學生全部查詢出來
 3              */
 4             List<Student> ls = (List<Student>)session.createQuery("from Student")
 5                                 .setFirstResult(0).setMaxResults(30).list();
 6             Iterator<Student> stus = ls.iterator();
 7             for(;stus.hasNext();)
 8             {
 9                 Student stu = (Student)stus.next();
10                 System.out.println(stu.getName());
11             }

控制臺輸出:

1 Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?

如果通過list()方法來獲得對象,毫無疑問,hibernate會發出一條sql語句,將所有的對象查詢出來,這沒什么問題。

用iterator()這種情況:

 1 /**
 2              * 如果使用iterator方法返回列表,對于hibernate而言,它僅僅只是發出取id列表的sql
 3              * 在查詢相應的具體的某個學生信息時,會再發出相應的SQL去取學生信息
 4              * 這就是典型的N+1問題
 5              * 存在iterator的原因是,有可能會在一個session中查詢兩次數據,如果使用list每一次都會把所有的對象查詢上來
 6              * 而是要iterator僅僅只會查詢id,此時所有的對象已經存儲在一級緩存(session的緩存)中,可以直接獲取
 7              */
 8             Iterator<Student> stus = (Iterator<Student>)session.createQuery("from Student")
 9                                 .setFirstResult(0).setMaxResults(30).iterate();
10             for(;stus.hasNext();)
11             {
12                 Student stu = (Student)stus.next();
13                 System.out.println(stu.getName());
14             }

在執行完上述的測試用例后,我們來看看控制臺的輸出,看會發出多少條 sql 語句:

Hibernate: select student0_.id as col_0_0_ from t_student student0_ limit ?
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
張一
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
肇慶
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
桑耳
.........

我們看到,當如果通過iterator()方法來獲得我們對象的時候,hibernate首先會發出1條sql去查詢出所有對象的 id 值,當我們如果需要查詢到某個對象的具體信息的時候,hibernate此時會根據查詢出來的 id 值再發sql語句去從數據庫中查詢對象的信息,這就是典型的?N+1?的問題。(簡單來說就是會有多一次去數據庫中查詢,解決思路很簡單,把那多余的一次的查詢不在數據庫中執行就可以了)

? ? ? ?那么這種 N+1 問題我們如何解決呢,其實我們只需要使用 list() 方法來獲得對象即可。

? ? ? 但是既然可以通過 list() 我們就不會出現 N+1的問題,那么我們為什么還要保留 iterator()這種形式呢? 嗯..............存在即合理。想想有沒有特殊的情況能發揮?iterator()的優勢????

? 如果我們需要在一個session當中要兩次查詢出很多對象,此時我們如果寫兩條 list()時,hibernate此時會發出兩條 sql 語句,而且這兩條語句是一樣的,

? 但是我們如果第一條語句使用 list(),而第二條語句使用 iterator()的話,此時我們也會發兩條sql語句,但是第二條語句只會將查詢出對象的id,所以相對應取出所有的對象而已,顯然這樣可以節省內存和減少與數據庫的交互(提升效率),而如果再要獲取對象的時候,因為第一條語句已經將對象都查詢出來了,此時會將對象保存到session的一級緩存中去,所以再次查詢時,就會首先去緩存中查找,如果找到,則不發sql語句了。這里就牽涉到了接下來這個概念:hibernate的一級緩存。

二、一級緩存(session級別)

我們來看看hibernate提供的一級緩存:

1 /**
2              * 此時會發出一條sql,將所有學生全部查詢出來,并放到session的一級緩存當中
3              * 當再次查詢學生信息時,會首先去緩存中看是否存在,如果不存在,再去數據庫中查詢
4              * 這就是hibernate的一級緩存(session緩存)
5              */
6             List<Student> stus = (List<Student>)session.createQuery("from Student")
7                                     .setFirstResult(0).setMaxResults(30).list();
8             Student stu = (Student)session.load(Student.class, 1);

我們來看看控制臺輸出:

?1 Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ??

我們看到此時hibernate僅僅只會發出一條 sql 語句,因為第一行代碼就會將整個的對象查詢出來,放到session的一級緩存中去,當我如果需要再次查詢學生對象時,此時首先會去緩存中看是否存在該對象,如果存在,則直接從緩存中取出,就不會再發sql了,但是要注意一點:hibernate的一級緩存是session級別的,所以如果session關閉后,緩存就沒了,此時就會再次發sql去查數據庫

 1 try
 2         {
 3             session = HibernateUtil.openSession();
 4             
 5             /**
 6              * 此時會發出一條sql,將所有學生全部查詢出來,并放到session的一級緩存當中
 7              * 當再次查詢學生信息時,會首先去緩存中看是否存在,如果不存在,再去數據庫中查詢
 8              * 這就是hibernate的一級緩存(session緩存)
 9              */
10             List<Student> stus = (List<Student>)session.createQuery("from Student")
11                                     .setFirstResult(0).setMaxResults(30).list();
12             Student stu = (Student)session.load(Student.class, 1);
13             System.out.println(stu.getName() + "-----------");
14         }
15         catch (Exception e)
16         {
17             e.printStackTrace();
18         }
19         finally
20         {
21             HibernateUtil.close(session);
22         }
23         /**
24          * 當session關閉以后,session的一級緩存也就沒有了,這時就又會去數據庫中查詢
25          */
26         session = HibernateUtil.openSession();
27         Student stu = (Student)session.load(Student.class, 1);
28         System.out.println(stu.getName() + "-----------");
1 Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ?
2 
3 Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?

我們看到此時會發出兩條sql語句,因為session關閉以后,一級緩存就不存在了,所以如果再查詢的時候,就會再發sql。要解決這種問題,我們應該怎么做呢?若按空間換取時間的思路,那能不能再來一個緩存?這就要我們來配置hibernate的二級緩存了,也就是sessionFactory級別的緩存。

三、二級緩存(sessionFactory級別)(簡單介紹)

如果我們只是取出對象的一些屬性的話,則不會將其保存到二級緩存中去,因為二級緩存緩存的僅僅是對象

由于學生對象已經緩存在二級緩存中了,此時再使用iterate來獲取對象的時候,首先會通過一條取id的語句,
然后在獲取去二級緩存中對象時,如果發現就不會再發SQL,這樣也就解決了N+1問題 而且內存占用也不多。

萬千叢中一點綠, 對象!,對象!,對象! 二級緩存就把第一次對象查詢攔截了,解決了N+1問題

?

轉載于:https://www.cnblogs.com/smellpawn/p/10809738.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386884.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386884.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386884.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

lambda函數 RUNOOB python練習題49

用來練手的python練習題&#xff0c;原題鏈接python練習實例49 該練習題主要是關于lambda函數的使用方法&#xff0c;本文就python中的lambda函數做出一點總結。 1. lambda函數的定義與調用 在python中&#xff0c;我們都知道使用def關鍵詞來定義一個函數, 例如一個最簡單的…

kubernetes(k8s)安裝部署

Kubernetes是一個開源的&#xff0c;用于管理云平臺中多個主機上的容器化的應用&#xff0c;Kubernetes的目標是讓部署容器化的應用簡單并且高效,Kubernetes提供了應用部署&#xff0c;規劃&#xff0c;更新&#xff0c;維護的一種機制。 Kubernetes一個核心的特點就是能夠自主…

react typescript 子組件調用父組件

//父組件 import * as React from reactimport { Input } from antdconst Search Input.Searchimport "./index.less"import Child from "./compon/list" interface IProps { MakeMoney?: () > void //暴露方法} export default class ProjectLis…

python random隨機數 RUNOOB python練習題50

用來練手的python練習題&#xff0c;原題鏈接: python練習實例50、 該練習題主要包含了random模塊隨機數的應用&#xff0c;下面給出幾個常用的模塊內函數。 1. 生成浮點型隨機小數 最簡單的&#xff0c;就是用random函數&#xff0c;生成 [0.0,1.0)[0.0, 1.0)[0.0,1.0)范圍…

Spring Cloud Eureka Consul使用和對比

Spring Cloud簡介 最大的區別是Eureka保證AP, Consul為CP。 Consul強一致性(C)帶來的是&#xff1a; 服務注冊相比Eureka會稍慢一些。因為Consul的raft協議要求必須過半數的節點都寫入成功才認為注冊成功 Leader掛掉時&#xff0c;重新選舉期間整個consul不可用。保證了強一致…

符號 RUNOOB python練習題 51

用來練手的python練習題&#xff0c;原題鏈接: python練習實例51 python中的 & 和 | 使用過程中&#xff0c;變量類型不同&#xff0c;這兩個符號的作用也不同。 1. 對于數字變量&#xff0c;&\&& 和 ∣|∣ 用于逐位運算 # 二進制逐位邏輯與門運算 a 0b110…

Eclipse里的快捷鍵

MyEclipse 快捷鍵1(CTRL) ------------------------------------- Ctrl1 快速修復 CtrlD: 刪除當前行 CtrlQ 定位到最后編輯的地方 CtrlL 定位在某行 CtrlO 快速顯示 OutLine CtrlT 快速顯示當前類的繼承結構 CtrlW 關閉當前Editer CtrlK 快速定位到下一個 CtrlE 快…

Python打印楊輝三角形 RUNOOB python練習題61

用來練手的python練習題&#xff0c;原題鏈接: python練習實例61 題干: 打印出楊輝三角形 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 實現代碼如下: import numpy as nptable…

使用Docker快速搭建Tensorflow開發環境

當我剛開始學習使用scikit-learn時&#xff0c;總是會出現各種各樣的包依賴問題&#xff0c;兜兜轉轉了一遍才全部安裝好&#xff0c;現在的機器學習算法開發者大都使用tensorflow、pytorch來實現自己的想法&#xff0c;但依然會面臨各種包版本和依賴的問題&#xff0c;有一段時…

Java服務GC參數調優案例

這段時間在整理jvm系列的文章&#xff0c;無意中發現本文&#xff0c;作者思路清晰通過步步分析最終解決問題。我個人特別喜歡這種實戰類的內容&#xff0c;經原作者的授權同意&#xff0c;將文章分享于此。原文鏈接&#xff1a;Java服務GC參數調優案例&#xff0c;下面為轉載此…

RUNOOB python 67 數組的元素互換

用來練手的Python練習題&#xff0c;原題鏈接:python練習實例67 題干: 輸入數組&#xff0c;最大的與第一個元素交換&#xff0c;最小的與最后一個元素交換&#xff0c;輸出數組 代碼如下: import numpy as nptable np.array([10,4,9,3,11,25,37,15,2,231,672,22]) #定義sw…

11.13 ethtool:查詢網卡參數

ethtool命令用于查詢或設置網卡參數。ethtool [devname][rootlinuxprobe ~]# ethtool eth0Settings for eth0:Supported ports: [ TP ]Supported link modes: 10baseT/Half 10baseT/Full 100baseT/Half 100baseT/Full 1000baseT/Full Supported pause frame use: NoSupports au…

微信小程序、微信公眾號、H5之間相互跳轉

一、小程序和公眾號 答案是&#xff1a;可以相互關聯。 在微信公眾號里可以添加小程序。 圖片有點小&#xff0c;我把文字打出來吧&#xff1a; 可關聯已有的小程序或快速創建小程序。已關聯的小程序可被使用在自定義菜單和模版消息等場景中。 公眾號可關聯同主體的10個小程…

數組元素前移后移 RUNOOB python練習題 68

用來練手的python練習題&#xff0c;原題鏈接: python練習實例68 題干: 有 n 個整數&#xff0c;使其前面各數順序向后移 m 個位置&#xff0c;最后 m 個數變成最前面的 m 個數 代碼如下: import numpy as np # 構造一個儲存了n個整數的numpy數組 def numbers_input(n):a n…

LRU緩存簡單實現

緩存接口定義 /*** 緩存接口* * author zhi**/ public interface ICache<K, V> {/*** 添加緩存數據* * param key* param value*/void put(K key, V value);/*** 獲取緩存數據* * param key* return*/V get(K key);/*** 刪除緩存數據* * param key* return*/V remove(K k…

Mac Eclipse安裝lombok

Lombok是一個可以通過注解的形式可以幫助消除一些必須但是顯得很臃腫的Java代碼的工具&#xff0c;通過使用對應的注解&#xff0c;可以在進行編譯源碼的時候生成對應的方法&#xff0c;比如類屬性的get/set/toString()/類的構造方法等. 下面記錄一下在Mac Eclipse是如何安裝Lo…

tf.reduce_sum()方法深度解析

首先看一下reduce_sum及其參數的注釋 : def tf.reduce_sum(input_tensor, axisNone, keepdimsFalse, nameNone) Computes the sum of elements across dimensions of a tensor. Reduces input_tensor along the dimensions given in axis. Unless keepdims is true, the rank o…

主成分分析(PCA)原理詳解_轉載

一、PCA簡介 1. 相關背景 在許多領域的研究與應用中&#xff0c;往往需要對反映事物的多個變量進行大量的觀測&#xff0c;收集大量數據以便進行分析尋找規律。多變量大樣本無疑會為研究和應用提供了豐富的信息&#xff0c;但也在一定程度上增加了數據采集的工作量&#xff0c;…

Mac cnpm裝包時提示Error: EACCES: permission denied解決辦法

Cnpm裝包時提示Error: EACCES: permission denied解決辦法 2018年03月04日 09:31:51 miniminixu 閱讀數&#xff1a;1598 版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/miniminixu/article/details/79434609 只需在cnpm …