Hibernate之二級緩存

一、二級緩存

       二級緩存主要是緩存的是實體對象。二級緩存需要sessionFactory來管理,它是進初級的緩存,所有人都可以使用,它是共享的。hibernate提供了一個簡單實現,用Hashtable做的,只能作爲我們的測試使用,商用還是需要第三方產品。Hibernate緩存策略的提供商主要是EHCacheOSCacheSwarmCacheJBoss TreeCache。下面我們採用EHCache緩存策略來演示二級緩存。

(1) 將ehcache.xml文件拷貝到src下

Ehcache的默認配置如下:

<defaultCache
        //緩存裏可以放10000個對象
        maxElementsInMemory="10000"
       //設置緩存是否過期,如果是true就是永遠不過期
        eternal="false"
       //一個對象被訪問後多長時間還沒有被訪問就失效
        timeToIdleSeconds="120"
       //對象存活時間,如果設置永不過期,這個設置就沒有意義
        timeToLiveSeconds="120"
      //溢出問題,如果設成true,緩存裏超過10000個對象就保存到磁盤裏
        overflowToDisk/>

當然我們也可以對某個對象單獨配置,配置與默認相同,只是要指明加載的對象名name="com.lsh.hibernate.Student"

(2)在hibernate.cfg.xml文件中加載緩存產品提供商:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
(3)啓用二級緩存,這也是它的默認配置
<property name="hibernate.cache.use_second_level_cache">true</property>
(4)指定哪些實體類使用二級緩存,這個可以在映射文件中採用<cache>標籤指定貨在hibernate.cfg.xml中指定,我採用在hibernate.cfg.xml中指定
<class-cache usage="read-only" class="com.lsh.hibernate.Student"/>
(5)Student類
package com.lsh.hibernate;

public class Student {

	private int id;
	
	private String name;

	private Classes classes;
	
	public Classes getClasses() {
		return classes;
	}

	public void setClasses(Classes classes) {
		this.classes = classes;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
(6)JUnit測試類
package com.lsh.hibernate;

import org.hibernate.CacheMode;
import org.hibernate.Session;
import junit.framework.TestCase;

public class CacheTest extends TestCase {

	/**
	 * 開啓二級緩存
	 * 
	 * 在兩個session中發load
	 */
	public void testCache1(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.load(Student.class, 10);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.load(Student.class, 10);
			//不會發出查詢語句,因爲配置二級緩存,session可以共享二級緩存中的數據
			//二級緩存是進程級的緩存
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}
	
	/**
	 * 開啓二級緩存
	 * 
	 * 在兩個session中發get
	 */
	public void testCache2(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.get(Student.class, 10);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.get(Student.class, 10);
			//不會發出查詢語句,因爲配置二級緩存,session可以共享二級緩存中的數據
			//二級緩存是進程級的緩存
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}
	
	/**
	 * 開啓二級緩存
	 * 
	 * 在兩個session中發load查詢,採用session中的sessionFactory管理二級緩存
	 */
	public void testCache3(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.load(Student.class, 1);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
		//管理二級緩存
		HibernateUtils.getSessionFactory().evict(Student.class, 1);
		
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Student student =(Student)session.load(Student.class, 1);
			
			//管理二級緩存
			//HibernateUtils.getSessionFactory().evict(Student.class);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}
	

	/**
	 * 開啓二級緩存
	 * 
	 * 一級緩存和二級緩存的交互
	 */
	public void testCache4(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			//禁止將一級緩存中的數據放到二級緩存中
			session.setCacheMode(CacheMode.IGNORE);
		
			Student student =(Student)session.load(Student.class, 1);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
		
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			
			Student student =(Student)session.load(Student.class, 1);
			
			//會發出查詢語句,因爲禁止了一級緩存和二級緩存的交互
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}
		
	/**
	 * 大批量的數據添加,如果清理了一級緩存,二級緩存也要清理,因爲二級緩存也會溢出
	 */
	public void testCache7(){
		Session session=null;
		try {
			session=HibernateUtils.getSession();
			session.beginTransaction();
			
			//禁止一級緩存和二級緩存交互
			session.setCacheMode(CacheMode.IGNORE);
			for (int i = 0; i <100; i++) {
				Student student =new Student();
				student.setName("張三" +i);
				session.save(student);
				if (i%20==0) {
					//執行sql語句,相當於把這些緩存全部保存到數據庫中了
					session.flush();
					//清楚緩存的內容
					session.clear();
				}
			}
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}	
}

測試的情況都是開啓二級緩存,在兩個session中做測試!

下面是對上面代碼做的一個總結整理:

(7)什麼樣的數據適合存放到二級緩存?

1)很少被修改的數據;

2)不是很重要的數據,允許出現偶爾併發

3)不會被併發訪問的數據

4)參考數據

二、查詢緩存

       一級緩存和二級緩存時緩存實體對象的,而查詢緩存時緩存普通屬性結果集的。它對實體對象的結果集會緩存id。查詢緩存的生命週期是直到關聯的表發生修改。
(1)修改hibernate.cfg.xml,來開啓查詢緩存,默認false是不起作用的
<property name="hibernate.cache.use_query_cache">true</property>
(2)必須在程序中啓用query.setCacheable(true);
(3)Junit測試類
package com.lsh.hibernate;

import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;

public class CacheTest extends TestCase {

	/**
	 * 開啓查詢,關閉二級緩存,採用query.list()查詢普通屬性
	 * 
	 * 在一個session中發query.list()查詢
	 */
	public void testCache1(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			List names =session.createQuery("select s.name from Student s").setCacheable(true).list();
			for (int i = 0; i < names.size(); i++) {
				String name=(String)names.get(i);
				System.out.println(name);
			}
			System.out.println("------------------------");
			//不會發出查詢語句,因爲啓用查詢緩存 
			names =session.createQuery("select s.name from Student s").setCacheable(true).list();
				for (int i = 0; i < names.size(); i++) {
					String name=(String)names.get(i);
					System.out.println(name);
				}
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
	}
	
	/**
	 * 開啓查詢,開啓二級緩存,採用query.list()查詢普通屬性
	 * 
	 * 在兩個session中發query.list()查詢
	 */
	public void testCache2(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			List names =session.createQuery("select s.name from Student s").setCacheable(true).list();
			for (int i = 0; i < names.size(); i++) {
				String name=(String)names.get(i);
				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();
			//不會發出查詢語句,因爲查詢緩存和session的生命週期沒有關係
			List names = session.createQuery("select s.name from Student s")
								.setCacheable(true)
								.list();
			for (int i=0; i<names.size(); i++) {
				String name = (String)names.get(i);
				System.out.println(name);
			}
			session.getTransaction().commit();
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
	}
	
	/**
	 * 開啓查詢,關閉二級緩存,採用query.iterator()查詢普通屬性
	 * 
	 * 在兩個session中發query.iterator()查詢
	 */
	public void testCache3(){
		Session session =null;
		try {
			session =HibernateUtils.getSession();
			session.beginTransaction();
			Iterator iter =session.createQuery("select s.name from Student s").setCacheable(true).iterate();
			while (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.iterator()查詢普通屬性它不會使用查詢緩存
			//查詢緩存只對query.list()起作用
			Iterator iter =session.createQuery("select s.name from Student s").setCacheable(true).iterate();
			while (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);
		}
	}
	
	
	/**
	 * 關閉查詢,關閉二級緩存,採用query.list()查詢實體
	 * 
	 * 在兩個session中發query.list()查詢
	 */
	public void testCache4() {
		Session session = null;
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			List students = session.createQuery("select s from Student s")
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.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.list()每次執行都會發出查詢語句
			List students = session.createQuery("select s from Student s")
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.getName());
			}
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
		
	}	
	
	
	/**
	 * 開啓查詢,關閉二級緩存,採用query.list()查詢實體
	 * 
	 * 在兩個session中發query.list()查詢
	 */
	public void testCache5() {
		Session session = null;
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			List students = session.createQuery("select s from Student s")
								.setCacheable(true)
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.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();
			
			//會發出n條查詢語句,因爲開啓了查詢緩存,關閉了二級緩存,那麼查詢緩存就會緩存實體對象的id
			//第二次執行query.list(),將查詢緩存中的id依次取出,分別到一級緩存和二級緩存中查詢相應的實體
			//對象,如果存在就使用緩存中的實體對象,否則根據id發出查詢學生的語句
			List students = session.createQuery("select s from Student s")
								.setCacheable(true)
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.getName());
			}
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
		
	}	
	
	/**
	 * 開啓查詢,開啓二級緩存,採用query.list()查詢實體
	 * 
	 * 在兩個session中發query.list()查詢
	 */
	public void testCache6() {
		Session session = null;
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			List students = session.createQuery("select s from Student s")
								.setCacheable(true)
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.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();
			
			//不再發出查詢語句,因爲配置了二級緩存和查詢緩存
			List students = session.createQuery("select s from Student s")
								.setCacheable(true)
								.list();
			for (int i=0; i<students.size(); i++) {
				Student studnet = (Student)students.get(i);
				System.out.println(studnet.getName());
			}
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
		
	}		
}

 下面是對上邊代碼的一個文字整理:

條件

現象

開啓查詢,關閉二級緩存,在一個session中採用query.list()查詢普通屬性

第二次查詢不會發出查詢語句,因爲啓用查詢緩存

開啓查詢,開啓二級緩存,在兩個session中採用query.list()查詢普通屬性

不會發出查詢語句,因爲查詢緩存和session的生命週期沒有關係

開啓查詢,關閉二級緩存,在兩個session中採用query.iterator()查詢普通屬性

會發出查詢語句,query.iterator()查詢普通屬性它不會使用查詢緩存,查詢緩存只對query.list()起作用

關閉查詢,關閉二級緩存,在兩個session中採用query.list()查詢實體

會發出查詢語句,默認query.list()每次執行都會發出查詢語句

開啓查詢,關閉二級緩存,在兩個session中採用query.list()查詢實體

會發出n條查詢語句,應爲開啓了查詢緩存,關閉了二級緩存,那麼查詢緩存就會緩存實體對象的id,第二次執行query.list(),將查詢緩存中的id一次取出,分別到一級緩存和二級緩存中查詢相應的對象,如果存在就使用緩存中的實體對象,否則根據id發出查詢學生的語句

開啓查詢,開啓二級緩存,兩個session中發query.list()查詢

不發出查詢語句,因爲配置了二級緩存和查詢緩存

   對於查詢緩存來說,它緩存的key就是查詢所用的HQLSQL語句。需要注意的是查詢緩存不僅要求所使用的HQL語句、SQL語句相同,甚至要求所傳入的參數也相同,這樣Hibernate才能從查詢緩存中取得數據。還需要強調的是在大部分情況下查詢緩存並不能提高應用的性能,甚至會降低應用性能。因此,在實際項目中請慎重使用查詢緩存。

   使用緩存,肯定是長時間不改變的數據,如果經常變化的數據放到緩存裏就沒有太大意義了。因爲經常變化,還是需要經常到數據庫裏查詢,那就沒有必要用緩存了。

   不要想當然的以爲緩存一定能提高性能,僅僅在你能夠駕馭它並且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不瞭解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出髒數據。 
   如果受不了hibernate的諸多限制,那麼還是自己在應用程序的層面上做緩存吧。
 
在越高的層面上做緩存,效果就會越好。就好像儘管磁盤有緩存,數據庫還是要實現自己的緩存,儘管數據庫有緩存,咱們的應用程序還是要做緩存。因爲底層的緩存它並不知道高層要用這些數據幹什麼,只能做的比較通用,而高層可以有針對性的實現緩存,所以在更高的級別上做緩存,效果也要好些吧。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章