Hibernate緩衝以及哪些操作會向緩衝中讀取和存放資料
Hibernate緩衝
Hibernate有兩級緩衝,分別是一級緩衝和二級緩衝。一級緩衝也叫Session級緩衝,預設情況下就可以用,無需配置。一級緩衝生命週期由Session對象決定,Session對象關閉,一級緩衝也就消失。二級緩衝也叫SessionFactory級緩衝,需要配置後才能使用。二級緩衝的生命週期比一級緩衝的生命週期長,由SessionFactory對象決定,SessionFactory對象關閉,二級緩衝也就消失。 哪些操作會向緩衝中讀取和存放資料。
測試之前先說一下Hibernate訪問資料庫,Hibernate對傳統的JDBC進行了封裝,一般我們訪問資料庫無非就是增、刪、改、查這四個操作,而這四個操作均通過各自的SQL陳述式完成,所以Hibernate對資料庫進行這四個操作時也離開不了SQL語句,如果我們配置Hibernate時配置了show_sql這個屬性的話,一旦Hibernate對資料庫進行了增、刪、改、查操作的話,控制台就會將執行的SQL語句列印出來,這也就是下面我們為什麼能將控制台有沒有列印SQL語句作為Hibernate有沒有訪問資料庫的原因。下面開始測試:
測試環境:
系統:Windows 10
JDK:1.8.102
Hibernate:5.2.10
軟體:MyEclipse 2016
我們先看查詢,查詢我們常見的有get()、load()以及HQL中的Query對象的list()方法(後面為方便,我們稱為query.list())和uniqueResult()方法(後面為方便,我們稱為query.uniqueResult())這些方法。
先上結果:
get()、load()均會向緩衝中存放以及讀取資料,而query.list()和query.uniqueResult()會向緩衝存放資料但不會從緩衝中讀取資料
get()方法:
測試之前先說一下get()方法是怎麼尋找資料的,它會先到一級緩衝中去找,沒有的話,它會到二級緩衝中繼續找,二級緩衝中還沒有的話,就會立馬產生相應的查詢SQL,並把這個SQL發送到資料庫,到資料庫中去找編號為7499的員工,如果資料庫中也沒找到的話,就會返回null。如果資料庫中有的話就會返回查詢結果,在它返回查詢結果的同時,它還會把查到的結果放進緩衝中(一二級緩衝都會放)。
測試get()方法代碼:
// get()會向緩衝中存放和讀取資料測試 public static void testGetCache(Session session){ try { Employee emp6=session.get(Employee.class, 7499); System.out.println(emp6); Employee emp7=session.get(Employee.class, 7499); System.out.println(emp7); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select employee0_.empno as empno1_0_0_, employee0_.ename as ename2_0_0_, employee0_.job as job3_0_0_, employee0_.sal as sal4_0_0_, employee0_.deptno as deptno5_0_0_, employee0_.hiredate as hiredate6_0_0_, employee0_.comm as comm7_0_0_, employee0_.mgr as mgr8_0_0_ from SCOTT.Emp employee0_ where employee0_.empno=?員工姓名:ALLEN 員工編號7499員工姓名:ALLEN 員工編號7499
代碼中我們查詢編號7499的員工查了2次,但運行結果中只列印了第一次查詢的SQL語句,說明第一次是到資料庫中查的,第二次沒有去資料庫中查,但第二次卻依然查到了編號7499的員工,原因是因為Hibernate的緩衝機制,第一次找到編號為7499的員工到後就會把7499的員工資訊放進緩衝中,當我們第二次再次尋找的7499的員工資訊時,由於在緩衝中已經找到了,所以就直接返回結果,就不會到資料庫中去查了,因此控制台並沒有列印第二次的SQL語句。也就是說第二次查詢是從緩衝中查(讀取)出來的。所以結果很明顯:get()會向緩衝中存放以及讀取資料。
get()測試結果:get()會向緩衝中存放以及讀取資料
load()方法:
load()和get()尋找資料的方式差不多,但又有點區別。load()也是先到一級緩衝中去找,沒找到的話繼續到二級緩衝中找,二級緩衝中還沒找到的話,它會返回一個查詢結果的代理對象,當後面我們用到查詢結果時,這個時候它才會產生相應的SQL語句,並把SQL發送到資料庫,到資料庫中去找,你後面如果沒用到查詢結果的話它不會產生查詢的SQL,不會到資料庫中去找。換句話說,就是你什麼時候用到查詢結果,他就什麼時候去資料庫中找,如果在資料庫中沒找到的話,就會報錯,會報org.hibernate.ObjectNotFoundException的異常,導致程式中斷。如果資料庫中有的話就會返回查詢結果,在它返回查詢結果的同時,它還會把查到的結果放進緩衝中(一二級緩衝都會放)
測試load()方法代碼:
// load()會向緩衝中存放和讀取資料測試 public static void testLoadCache(Session session){ try { Employee emp6=session.get(Employee.class, 7369); System.out.println(emp6); Employee emp7=session.get(Employee.class, 7369); System.out.println(emp7); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select employee0_.empno as empno1_0_0_, employee0_.ename as ename2_0_0_, employee0_.job as job3_0_0_, employee0_.sal as sal4_0_0_, employee0_.deptno as deptno5_0_0_, employee0_.hiredate as hiredate6_0_0_, employee0_.comm as comm7_0_0_, employee0_.mgr as mgr8_0_0_ from SCOTT.Emp employee0_ where employee0_.empno=?員工姓名:SMITH 員工編號7369員工姓名:SMITH 員工編號7369
代碼中我們同樣查詢編號7369的員工查了2次,但運行結果中只列印了第一次查詢的SQL語句,說明第一次是到資料庫中查的,第二次沒有去資料庫中查,但第二次卻依然查到了編號7369的員工,原因也是因為Hibernate的緩衝機制,第一次找到編號為7369的員工後就會把7369的員工資訊放進了緩衝,第二次尋找的7369的員工資訊是從緩衝中查(讀取)出來的。所以load()也會向緩衝中存放以及讀取資料。
load()方法測試結果:load()會向緩衝中存放以及讀取資料
query.list()方法
測試query.list()方法代碼:
// 測試query.list()會向緩衝中存放資料但不會從緩衝的讀取資料 public static void testQueryListCache(Session session) { try { String hql="from Employee"; Query query=session.createQuery(hql); List<Employee> list=query.list(); for (int i=0;i<2;i++) { System.out.println("員工編號:"+list.get(i).getEmpNo()+" 員工姓名:"+list.get(i).getEmpName()); } System.out.println("------------------------------------------"); Employee emp=(Employee)session.get(Employee.class, 7654); System.out.println("員工編號:"+emp.getEmpNo()+" 員工姓名:"+emp.getEmpName()); System.out.println("------------------------------------------"); String hql2="from Employee"; Query query2=session.createQuery(hql2); List<Employee> list2=query2.list(); for (int i=0;i<2;i++) { System.out.println("員工編號:"+list2.get(i).getEmpNo()+" 員工姓名:"+list2.get(i).getEmpName()); } } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select employee0_.empno as empno1_0_, employee0_.ename as ename2_0_, employee0_.job as job3_0_, employee0_.sal as sal4_0_, employee0_.deptno as deptno5_0_, employee0_.hiredate as hiredate6_0_, employee0_.comm as comm7_0_, employee0_.mgr as mgr8_0_ from SCOTT.Emp employee0_員工編號:20 員工姓名:empSu2員工編號:19 員工姓名:empSave2------------------------------------------員工編號:7654 員工姓名:MARTIN------------------------------------------Hibernate: select employee0_.empno as empno1_0_, employee0_.ename as ename2_0_, employee0_.job as job3_0_, employee0_.sal as sal4_0_, employee0_.deptno as deptno5_0_, employee0_.hiredate as hiredate6_0_, employee0_.comm as comm7_0_, employee0_.mgr as mgr8_0_ from SCOTT.Emp employee0_員工編號:20 員工姓名:empSu2員工編號:19 員工姓名:empSave2
測試代碼中我們用query.list()方法去查詢所有員工的編號和姓名查了兩次(為了方便看結果,迴圈列印員工的編號和姓名時只迴圈了2次),分別在第一條虛線前和第二條虛線後。兩條虛線之間我們用get()的方式尋找了其中一名員工,看兩條虛線間有沒有列印get()查詢員工的SQL語句來驗證第一次query.list()查詢後,有沒有向緩衝中存放資料。
運行結果可以看出,控制台將這兩次query.list()查詢所有員工的SQL語句都列印了出來,說明兩次query.list()查詢都是到資料庫中尋找的。兩次query.list()查詢之間我們用get()去查詢了編號為7654的員工,運行結果顯示我們查到了該員工的資訊,但控制台卻沒有列印查詢該員工資訊的SQL語句,說明get()沒有到資料庫中去查,get()查到的編號為7654的員工資訊不是從資料庫中查到的,由於get()尋找資料的順序是先從緩衝中找再到資料庫中找,但我們用get()卻查到了編號為7654的員工,說明get()查到的資料是從緩衝中查到(讀取到)的,觀察整段測試代碼,get()查詢之前我們只進行了query.list()查詢操作,所以很明顯緩衝中的資訊是query.list()查到結果後放進去的,這就說明了query.list()會向緩衝中存放資料,此時緩衝中已經有了所有員工資訊,但get()後面的query.list()查詢操作在緩衝中已經有要尋找的資訊時依舊是到資料庫中查的,所以會發現query.list()並不會從緩衝中尋找(讀取)資料。綜上:測試query.list()會向緩衝中存放資料但不會從緩衝的讀取資料
query.list()測試結果:query.list()會向緩衝中存放資料但不會從緩衝的讀取資料
query.uniqueResult()方法:
測試query.uniqueResult()代碼:
//測試query.uniqueResult()會向緩衝中存放資料但不會從緩衝的讀取資料 public static void testQueryUniqueResultCache(Session session) { try { String hql="from Employee where empNo=7876"; Query query=session.createQuery(hql); Employee emp=(Employee)query.uniqueResult(); System.out.println("員工編號:"+emp.getEmpNo()+" 員工姓名:"+emp.getEmpName()); System.out.println("-------------------------------------------------"); //通過get()來測試query.list()有沒有向緩衝中存放資料 Employee emp3=(Employee)session.get(Employee.class, 7876); System.out.println("員工編號:"+emp3.getEmpNo()+" 員工姓名:"+emp3.getEmpName()); System.out.println("-------------------------------------------------"); String hql2="from Employee where empNo=7876"; Query query2=session.createQuery(hql2); Employee emp2=(Employee)query.uniqueResult(); System.out.println("員工編號:"+emp2.getEmpNo()+" 員工姓名:"+emp2.getEmpName()); } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select employee0_.empno as empno1_0_, employee0_.ename as ename2_0_, employee0_.job as job3_0_, employee0_.sal as sal4_0_, employee0_.deptno as deptno5_0_, employee0_.hiredate as hiredate6_0_, employee0_.comm as comm7_0_, employee0_.mgr as mgr8_0_ from SCOTT.Emp employee0_ where employee0_.empno=7876員工編號:7876 員工姓名:ADAMS-------------------------------------------------員工編號:7876 員工姓名:ADAMS-------------------------------------------------Hibernate: select employee0_.empno as empno1_0_, employee0_.ename as ename2_0_, employee0_.job as job3_0_, employee0_.sal as sal4_0_, employee0_.deptno as deptno5_0_, employee0_.hiredate as hiredate6_0_, employee0_.comm as comm7_0_, employee0_.mgr as mgr8_0_ from SCOTT.Emp employee0_ where employee0_.empno=7876員工編號:7876 員工姓名:ADAMS
與上面的測試query.list()類似,我們用query.uniqueResult()查編號為7876的員工查了兩次,分別是在第一條虛線前和 第二條虛線後。兩條虛線中間我們用get()再次尋找編號7876的員工,看兩條虛線之間有沒有列印get()查詢的SQL語句來判斷query.uniqueResult()有沒有向緩衝中放入資料。
結果顯示兩條虛線之外均有列印查詢的SQL,說明兩次query.uniqueResult()查詢都是到資料庫中去查的,從運行結果來看,兩條虛線之間並沒有列印SQL語句,說明我們用get()尋找員工7876時並沒有到資料庫中去查,但是依然查到了員工資訊,根據get()尋找資料的順序結合此時get()並沒有到資料庫中去查,可以知道,get()查到員工資訊是從緩衝中查(讀取)到的,依舊是觀察整段測試代碼,get()查詢員工前面除了query.uniqueResult()查詢操作外並沒有進行其它任何操作,所以,很顯然,緩衝中的員工資訊是前面query.uniqueResult()查到結果後放進去的。到這就說明了query.uniqueResult()會向緩衝中存放資料。
再來,結合get()後面的query.uniqueResult()查詢操作,在緩衝中已經有要尋找的員工時,卻還是到資料庫中去尋找。所以得出結論:query.uniqueResult()不會從緩衝的讀取資料。
綜合就是:query.uniqueResult()會向緩衝中存放資料但不會從緩衝的讀取資料
測試結果:query.uniqueResult()會向緩衝中存放資料但不會從緩衝的讀取資料
說完了查詢,我們說添加,添加我們常見的有save()和saveOrUpdate()
先上結果:
save()和saveOrUpdate()進行添加時,均會向緩衝中存放資料。
save()方法:
測試save()方法代碼
//測試save()會向緩衝中存放資料 public static void testSaveCache(Session session) { try { Transaction tr=session.beginTransaction(); Employee emp1=new Employee(); emp1.setEmpName("empSave2"); session.save(emp1); System.out.println("添加的對象的主鍵為:"+emp1.getEmpNo()); Employee emp2=(Employee)session.get(Employee.class,emp1.getEmpNo()); System.out.println("員工姓名:"+emp2.getEmpName()); tr.commit();//只有當這句代碼執行時,才會向資料庫發送sql } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select hibernate_sequence.nextval from dual添加的對象的主鍵為:19員工姓名:empSave2Hibernate: insert into SCOTT.Emp (ename, job, sal, deptno, hiredate, comm, mgr, empno) values (?, ?, ?, ?, ?, ?, ?, ?)
從代碼可以看出,我們在tr.commit();這句代碼之前我們又用get()查詢了當前添加的對象,此時tr.commit();還沒有執行,也就是事務還沒提交,所以對象這個時候還沒存進資料庫,按理說我們應該查不到記錄。
但從運行結果中可以看出,我們查到了資料,而且,控制台沒有列印相關的查詢SQL,說明我們用get()尋找的員工資訊不是從資料庫中找的,結合get()尋找資料是先從緩衝再到資料庫中找的順序,所以我們能得出,查到的資料是從緩衝中查到的,而在我們get()查詢之前除了save()操作並沒有其它操作,因此,緩衝中的資料是save()放的,也就是說在我們調用save()添加時它會把添加的資訊放進緩衝裡。
save()測試結果:save()會向緩衝中存放資料
saveOrUpdate()方法:
測試saveOrUpdate()方法代碼
//測試saveOrUpdate()進行添加時會向緩衝中存放資料 public static void testSaveOrUpdateCache(Session session) { try { Transaction tx=session.beginTransaction(); Employee emp3=new Employee(); emp3.setEmpName("empSu2"); session.saveOrUpdate(emp3); System.out.println("添加的對象的主鍵為:"+emp3.getEmpNo()); Employee emp4=(Employee)session.get(Employee.class,emp3.getEmpNo()); System.out.println("員工姓名:"+emp4.getEmpName()); tx.commit();//只有當這句代碼執行時,才會向資料庫發送sql } catch (Exception e) { e.printStackTrace(); }finally{ session.close(); } }
運行結果:
Hibernate: select hibernate_sequence.nextval from dual添加的對象的主鍵為:20員工姓名:empSu2Hibernate: insert into SCOTT.Emp (ename, job, sal, deptno, hiredate, comm, mgr, empno) values (?, ?, ?, ?, ?, ?, ?, ?)
和測試save()一樣,我們在tx.commit();這句代碼之前我們又用get()查詢了當前添加的對象,此時tr.commit();還沒有執行,也就是事務還沒提交,所以對象還沒存進資料庫,按理說我們應該查不到記錄。
運行結果也和上面save()的一樣,控制台並沒有列印相關的查詢SQL說明,說明我們用get()尋找的員工資訊不是從資料庫中找的,結合get()尋找資料是先從緩衝再到資料庫中找的順序,所以我們能得出,查到的資料是從緩衝中查到的,而在我們get()查詢之前除了saveOrUpdate()操作並沒有其它操作,因此,緩衝中的資訊是saveOrUpdate()操作放進去的,所以,saveOrUpdate()也會向緩衝中存放資料。
saveOrUpdate()測試結果:saveOrUpdate()會向緩衝中存放資料
以上均為個人理解,如有錯誤,歡迎指正