一、簡(jiǎn)介
- 查詢緩存(工程
hibernate_query_cache
)是針對(duì)普通屬性結(jié)果集的緩存,對(duì)實(shí)體對(duì)象的結(jié)果集只緩存id。 - 查詢緩存的生命周期:當(dāng)前關(guān)聯(lián)的表發(fā)生修改,查詢緩存立即失效。
二、查詢緩存的配置和使用
- 在hibernate.cfg.xml文件中啟用查詢緩存,如:
<property name="hibernate.cache.use_query_cache">true</property>
,因?yàn)閔ibernate中查詢緩存默認(rèn)是關(guān)閉的,這和二級(jí)緩存不一樣。 - 在程序中必須手動(dòng)啟用查詢緩存,如:
query.setCacheable(true);
測(cè)試:
使用的相關(guān)實(shí)體類和映射和二級(jí)緩存中使用的是一樣的。只需要在hibernate的配置文件中開啟查詢緩存即可。
/**
* 開啟查詢緩存,關(guān)閉二級(jí)緩存
*
* 開啟一個(gè)session,分別調(diào)用query.list
*/
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
List names = query.list();
for (Iterator iter=names.iterator();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
System.out.println("-------------------------------------");
query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
//沒有發(fā)出查詢sql,因?yàn)閱⒂昧瞬樵兙彺? names = query.list();
for (Iterator iter=names.iterator();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:這個(gè)例子中我們可以看到如果沒有開啟二級(jí)緩存和查詢緩存,那么會(huì)發(fā)出兩次sql,但是如果我們將查詢緩存打開,同時(shí)在程序中開啟,那么在第二次使用list查詢普通屬性的時(shí)候是不會(huì)再次發(fā)出sql的。
/**
* 開啟查詢緩存,關(guān)閉二級(jí)緩存
*
* 開啟兩個(gè)session,分別調(diào)用query.list
*/
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
List names = query.list();
for (Iterator iter=names.iterator();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
//不會(huì)發(fā)出查詢sql,因?yàn)椴樵兙彺娴纳芷诤蛃ession無(wú)關(guān)
List names = query.list();
for (Iterator iter=names.iterator();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:這個(gè)例子主要是為了說(shuō)明查詢緩存的聲明周期和session是無(wú)關(guān)的,而這里又是查詢的普通屬性,所以在第二次使用list方法的時(shí)候不會(huì)發(fā)出sql語(yǔ)句。
/**
* 開啟查詢緩存,關(guān)閉二級(jí)緩存
*
* 開啟兩個(gè)session,分別調(diào)用query.iterate
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
for (Iterator iter=query.iterate();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s.name from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
//查詢緩存只對(duì)query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
//查詢緩存
for (Iterator iter=query.iterate();iter.hasNext(); ) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:這個(gè)例子主要為了說(shuō)明查詢緩存只對(duì)list方法起作用,而對(duì)iterate方法是不起作用的。
/**
* 關(guān)閉查詢緩存,關(guān)閉二級(jí)緩存
*
* 開啟兩個(gè)session,分別調(diào)用query.list查詢實(shí)體對(duì)象
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
//query.setCacheable(true);
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
//query.setCacheable(true);
//會(huì)發(fā)出查詢sql,因?yàn)閘ist默認(rèn)每次都會(huì)發(fā)出查詢sql
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:之前我們都是查詢的普通屬性,這里我們是查詢實(shí)體類。在使用list查詢實(shí)體對(duì)象的時(shí)候每次都會(huì)發(fā)出sql,即使在同一個(gè)session中也是一樣,即默認(rèn)是每次都發(fā)出sql語(yǔ)句。
/**
* 開啟查詢緩存,關(guān)閉二級(jí)緩存
*
* 開啟兩個(gè)session,分別調(diào)用query.list查詢實(shí)體對(duì)象
*/
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
//會(huì)發(fā)出n條查詢語(yǔ)句,因?yàn)殚_啟了查詢緩存,關(guān)閉了二級(jí)緩存,那么查詢緩存會(huì)緩存實(shí)體對(duì)象的id
//所以hibernate會(huì)根據(jù)實(shí)體對(duì)象的id去查詢相應(yīng)的實(shí)體,如果緩存中不存在相應(yīng)的
//實(shí)體那么將發(fā)出根據(jù)實(shí)體id查詢的sql語(yǔ)句,否則不會(huì)發(fā)出sql使用緩存中的數(shù)據(jù)
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:這里我們開啟了查詢緩存,那么在第二次使用list方法的時(shí)候就會(huì)出現(xiàn)N+1問(wèn)題,因?yàn)椴樵兙彺鏁?huì)保存普通屬性id,所以出現(xiàn)N+1問(wèn)題。
/**
* 開啟查詢緩存,開啟二級(jí)緩存
*
* 開啟兩個(gè)session,分別調(diào)用query.list查詢實(shí)體對(duì)象
*/
public void testCache6() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Query query = session.createQuery("select s from Student s");
//啟用查詢查詢緩存
query.setCacheable(true);
//不會(huì)發(fā)出查詢sql,因?yàn)殚_啟了二級(jí)緩存和查詢緩存,查詢緩存緩存了實(shí)體對(duì)象的id列表
//hibernate會(huì)根據(jù)實(shí)體對(duì)象的id列表到二級(jí)緩存中取得相應(yīng)的數(shù)據(jù)
List students = query.list();
for (Iterator iter=students.iterator();iter.hasNext(); ) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
說(shuō)明:這里我們將查詢緩存和二級(jí)緩存之后使用list方法去查詢實(shí)體對(duì)象,那么在第二次使用list方法去查詢的時(shí)候就不會(huì)再次發(fā)出sql語(yǔ)句了。
最后:
1.查詢緩存的生命周期和session的生命周期沒有任何關(guān)系;
2.和一、二級(jí)緩存一樣,查詢緩存對(duì)迭代query.iterate()不起作用,只對(duì)list起作用。
3.開啟查詢緩存,關(guān)閉二級(jí)緩存,開啟兩個(gè)session,分別調(diào)用query.list查詢實(shí)體對(duì)象時(shí),在第二次查詢時(shí)會(huì)發(fā)出N條查詢語(yǔ)句,這是因?yàn)殚_啟了查詢緩存,關(guān)閉了二級(jí)緩存,那么查詢緩存會(huì)緩存實(shí)體對(duì)象的id,之后再次查詢時(shí),Hibernate會(huì)根據(jù)實(shí)體對(duì)象的id去查詢相應(yīng)的實(shí)體,如果緩存中不存在相應(yīng)的實(shí)體,那么將根據(jù)實(shí)體id發(fā)出N條查詢語(yǔ)句,否則直接從緩存中取得相應(yīng)的數(shù)據(jù),不會(huì)發(fā)出sql。而如果開啟了二級(jí)緩存則不會(huì)出現(xiàn)上面的N+1問(wèn)題。
4.由于當(dāng)前關(guān)聯(lián)的表發(fā)生修改的時(shí)候查詢緩存立即失效,所以其利用率不高,比如第一次查詢高一班到高三班的所有學(xué)生,而在此過(guò)程中高二班學(xué)生有變化,那么查詢緩存就會(huì)失效,這樣導(dǎo)致利用率比較低。