Hibernate旅程(七)Hibernate快取機制--一級快取
Hibernate一級快取
快取就是你去小賣鋪買東西,不用再去生產車間裡買東西,當小賣鋪倒閉了,也就是session快取生命週期結束。hibernate一級快取的宣告週期很短,和session的生命週期一致,hibernate的一級快取也叫做session級快取,或叫事務級快取。下面來看session控制的一級快取。
同一session中使用兩次load()進行查詢。
程式碼入下所示,我們在同一個session中兩次呼叫load()。
/** * 在同一個session中發出兩次load查詢 */ public voidtestCache1() { Sessionsession = null; try { //使用load查詢兩遍. session= HibernateUtils.getSession(); session.beginTransaction(); Studentstudent =(Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); //不會發出查詢語句,load使用快取,在同一個session中. student =(Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
兩次都採用load()進行載入並打印出學生的名字。發出的sql語句如下所示。對於loadlazy載入,只有在使用load載入上來的類,才真正的去資料庫查詢。控制檯列印的sql程式碼如下所示。
從顯示結果中我們可以看出,在第一次真正使用load的時候,發出sql語句。Hibernate會把查詢上來真實的物件放到session的map中。當第二次load再次使用的時候,不會再發送sql語句,而是直接從session的快取中取出。
同一session中使用兩次get()進行查詢。
原始碼也就是把上述load換成get。
Get和load的區別,get不支援延遲載入而load支援延遲載入,但當我們在同一次訪問中訪問兩次
在同一session中發出兩次iterate查詢,查詢實體物件。
程式碼如下所示。
/** * 在同一個session中發出兩次iterate查詢,查詢實體物件 * 發出兩次迭代查詢.查詢實體物件. */ public void testCache3() { Sessionsession = null; try { session= HibernateUtils.getSession(); session.beginTransaction(); Iteratoriter = session.createQuery("from Student s where s.id<5").iterate(); while(iter.hasNext()) { Studentstudent = (Student)iter.next(); System.out.println(student.getName()); } System.out.println("--------------------------------------"); //它會發出查詢id的語句,但不會發出根據id查詢學生的語句,因為iterate使用快取 iter= session.createQuery("from Student s where s.id<5").iterate(); while(iter.hasNext()) { Studentstudent = (Student)iter.next(); System.out.println(student.getName()); } session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
執行結果如下所示。
第一次呼叫迭代器的查詢時,首先發出查詢id的語句,並根據id查詢學生。當第二次呼叫時,只會發出查詢id的語句,不會再根據id來查詢對應的學生物件。這說明iterate(迭代器)是支援快取的。
在同一session中發出兩次iterate查詢,查詢實體物件。
程式碼如下所示。
Session session = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Iteratoriter = session.createQuery("select s.name from Student s wheres.id<5").iterate();
while(iter.hasNext()) {
Stringname = (String)iter.next();
System.out.println(name);
}
System.out.println("--------------------------------------");
//iterate查詢普通屬性,一級快取不會快取,所以發出查詢語句
//一級快取是快取實體物件的
iter= session.createQuery("select s.name from Student s wheres.id<5").iterate();
while(iter.hasNext()) {
Stringname = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
顯示結果如下所示。
根據顯示結果可知,迭代器查詢普通屬性,一級快取不會儲存,所以當第二次查詢的時候仍然發出查詢語句。這說明iterate一級快取快取的是實體物件,對於普通屬性不會快取。
在兩個session中發出load查詢。
程式碼如下所示。
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = (Student)session.load(Student.class, 1);
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = (Student)session.load(Student.class, 1);
//會發出查詢語句,session間不能共享一級快取資料
//因為他會伴隨著session的消亡而消亡
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
顯示結果如下所示。
從上圖可以看出,會發出兩次sql,這也說明了session之間不能共享一級快取資料,因為快取會本隨著自己的那個session的消亡而消亡。
在一個session中先呼叫save(),再呼叫get或load查詢剛剛save的資料。
程式碼如下所示。
/**
* 在同一個session中先呼叫save,再呼叫load查詢剛剛save的資料
*/
public voidtestCache6() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = new Student();
student.setName("張三");
Serializableid = session.save(student);
student= (Student)session.load(Student.class,id);
//不會發出查詢語句,因為save支援快取
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
顯示結果如下所示。
從圖中可以看出,只發送一次插入語句,當我們再次查詢的時候,沒有去資料庫進行查詢,這說明當使用session.save()時,已經放入快取中。再進行查詢時會從快取中取出。
大批量資料的新增。
程式碼如下所示。
public voidtestCache7() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
for (int i=0;i<100; i++) {
Studentstudent = newStudent();
student.setName("張三" +i);
session.save(student);
//每20條更新一次
if (i %20 == 0) {
session.flush();
//清除快取的內容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
列印sql如下圖所示。
從圖中可知,列印了100條inset語句,在一個session中快取100條資料很大,我們可以設定每20條清空快取。
Hibernate一級快取總結
從上可知,load、get、iterate查詢實體物件時,支援一級快取,但查詢普通屬性時不支援一級快取,當我們大批量資料插入或更新時,由於快取中資料量太大,我們可以設定快取中的條數,使用session.clear()來清除快取。