Hibernate緩存機制

什麼是緩存:

緩存是介於應用程序和物理數據源之間,緩存內的數據是對物理數據源中的數據的複製,其做用是爲了下降應用程序對物理數據源訪問的頻次,從而提升了應用的運行性能。java

1.緩存的範圍:

1.1 事務範圍:緩存只能被當前事務訪問。緩存的生命週期依賴於事務的生命週期,當事務結束時,緩存也就結束生命週期。在此範圍下,緩存的介質是內存。事務能夠是數據庫事務或者應用事務,每一個事務都有獨自的緩存,緩存內的數據一般採用相互關聯的的對象形式。

1.2進程範圍:緩存被進程內的全部事務共享。這些事務有多是併發訪問緩存,所以必須對緩存採起必要的事務隔離機制。緩存的生命週期依賴於進程的生命週期,進程結束時,緩存也就結束了生命週期。進程範圍的緩存可能會存放大量的數據,因此存放的介質能夠是內存或硬盤。緩存內的數據既能夠是相互關聯的對象形式也能夠是對象的鬆散數據形式。鬆散的對象數據形式有點相似於對象的序列化數據,可是對象分解爲鬆散的算法比對象序列化的算法要求更快。

1.3 集羣範圍:在集羣環境中,緩存被一個機器或者多個機器的進程共享。緩存中的數據被複制到集羣環境中的每一個進程節點,進程間經過遠程通訊來保證緩存中的數據的一致性,緩存中的數據一般採用對象的鬆散數據形式。算法

2.Hibernate:一級緩存

:緩存位置:session中
:生命週期:一個事務中。
:緩存規格:{ID:實體}
:默認開啓
2.1 什麼時候數據會進入緩存:事務中加載過的數據,都會進入緩存,並以{ID:實體}存儲在session中。

2.2 什麼時候能夠檢查緩存:以ID爲條件的查詢能夠檢查緩存。
*session.get();//能夠檢查
*Query.list();//不能檢查
.uniqueResult();//不能檢查
.iterate();//能夠檢查
*細節:iterate()運做流程
String hql="from User u where u.name=?";
Query.iterate(hql);
1>保留查詢條件,到數據庫中查詢ID,
select id from t_user where t_name=?
[1,2,3,4,5]
2>經過查到的ID去檢查緩存。若是有緩存可用,則不用再查詢數據庫。
可是,注意,若是沒有緩存可用,則要再次發起對數據庫的查詢:
select * from t_user where t_id=5;
select * from t_user where t_id=4;
select * from t_user where t_id=3;
select * from t_user where t_id=2;
select * from t_user where t_id=1;
綜上,再使用iterate()方法時,可能致使n+1次查詢問題。n=知足條件的數據行數。
3>使用:
Iterator it=query2.iterate();
while(it.hasNext()){
User user=(User)it.next();
System.out.println(user);
}
==================================================================================sql

3.hibernate:二級緩存

:緩存位置:SessionFactory中
:生命週期:全局可用
:緩存規格:{ID:實體}
:默認關閉:經過配置開啓。
:*開啓二級緩存
<!-- 開啓二級緩存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 二級緩存類別:EhCache,OSCache,JbossCache -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
*導包 並 引入ehcahe.xml
*爲要進入二級緩存的實體,增長權限。
//只讀緩存權限
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
//讀寫緩存權限
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
3.1 什麼時候數據會進入緩存:事務中加載過的數據,都會進入緩存,並以{ID:實體}存儲在session中。

3.2 什麼時候能夠檢查緩存:以ID爲條件的查詢能夠檢查緩存。
get();
iterate();
==================================================================================數據庫

4.hibernate: 查詢緩存:依賴二級緩存

: 緩存位置:SessionFactory中
: 生命週期:全局可用,但不穩定,若是和緩存數據相關的表有任何的改動,則緩存數據失效,在通常的應用程序中,sessionfactory會以單例的形式存在,因此在整個應用程序的生命週期裏,sessionfactory會一直存在。既二級緩存也一直存在直到關閉應用程序
: 緩存規格: {hql:查詢結果(字段)}
: 默認關閉:<property name="hibernate.cache.use_query_cache">true</property>
在查詢前://本次查詢要使用查詢緩存
query.setCacheable(true);

4.1 什麼時候數據會進入緩存:用hql查詢字段的查詢結果,均可以進入查詢緩存:{HQL:結果(字段)}

4.2 什麼時候能夠檢查緩存:只要再次用一樣的hql查詢,則能夠檢查查詢緩存。緩存

4.3 使用場景session

什麼樣的數據適合存放到第二級緩存中?
  一、不多被修改的數據 
  二、不是很重要的數據,容許出現偶爾併發的數據
  三、不會被併發訪問的數據
  四、參考數據併發

不適合存放到第二級緩存的數據?
  一、常常被修改的數據
  二、財務數據,絕對不容許出現併發
  三、與其餘應用共享的數據。

*細節:若是查詢的實體,則查詢緩存只能緩存:{HQL:實體的ID字段}
==================================================================================oracle

5.緩存總結:

5.1 緩存規格:
*一級緩存,二級緩存,緩存的是實體:{ID:實體}
session.get(User.class,1);
"from User u";
*查詢緩存:{HQL:查詢結果}
tx
"select u.id,u.age from User u";
commit();
5.2 使用:
*若是查詢時是查詢字段的話:select a,b,c,d from XXX;
查詢緩存足矣。
*若是查詢時是查詢實體的話:from User u;
二級緩存+查詢緩存。
5.3 查詢緩存和二級緩存的聯合使用:
*若是查詢時是查詢實體的話:from User u;
初次query.list();時,檢查查詢緩存,沒有可用數據,則轉向數據庫,得到一個實體User,
將{HQL:User的ID}存入查詢緩存,將{User的ID:User}存入二級緩存
再次query.list();時,檢查查詢緩存,得到緩存數據:User的ID,經過UserID檢查二級緩存,
若是有數據,則直接使用,不然以各個ID爲條件分別發起查詢app

==================================================================================ide

6. 爲何這樣設計

通常狀況下,咱們查詢的數據通常是實時的,使用二級緩存確定不行,使用一級緩存既利用了緩存又不會影響實時。使用二級緩存是爲了存儲一些比較穩定的數據,二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無做用。爲此,Hibernate提供了針對條件查詢的Query緩存。
 

==================================================================================

7.圖解緩存

分區:是以全類名爲單位分區

==================================================================================

8.測試代碼

插件緩存用的是ecache

實體:

package com.c50.entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name="user50")
//只讀緩存權限
//@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
//讀寫緩存權限
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class User {
    @Id
    @Column(name="id")
    @GenericGenerator(name="inc50",strategy="increment")
    @GeneratedValue(generator="inc50")
    private Integer userID;
    private String name ;
    private Integer age;
    @Column(name="birth")
    @Temporal(TemporalType.DATE)
    private Date birthday;
    

    public User(){}
    
    public User(Integer userID, String name, Integer age, Date birthday) {
        super();
        this.userID = userID;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public Integer getUserID() {
        return userID;
    }
    public void setUserID(Integer userID) {
        this.userID = userID;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User [userID=" + userID + ", name=" + name + ", age=" + age
                + ", birthday=" + birthday + "]";
    }
    
}

二級緩存ecache:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
      maxEntriesLocalHeap: 在內存中緩存的element的最大數目。 
      maxEntriesLocalDisk: 在磁盤上緩存的element的最大數目,默認值爲0,表示不限制。 
      eternal: 設定緩存的elements是否永遠不過時。若是爲true,則緩存的數據始終有效,
                  若是爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷。 
      <persistence strategy="localTempSwap"/>  內存存滿後將數據存入硬盤
      timeToIdleSeconds="10"  緩存空閒時間   默認值0 一直存活
      timeToLiveSeconds="15"  緩存最大存活時間    默認值0 一直存活
      diskExpiryThreadIntervalSeconds:磁盤數據的有效時間
      memoryStoreEvictionPolicy="LFU"
          FIFO ,first in first out (先進先出).
        LFU , Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個hit 屬性,hit 
               值最小的將會被清出緩存。
        LRU ,Least Recently Used(最近最少使用). (ehcache 默認值).緩存的元素有一個時間戳,當緩存容量滿了,
                而又須要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。
 -->

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd">

    <!-- <diskStore path="java.io.tmpdir"/> -->
    <diskStore path="E:\\cache4"/>
    <defaultCache
            maxEntriesLocalHeap="2"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
</ehcache>

hibernate配置文件:

<?xml version="1.0" encoding="utf-8"?>
<!-- 
    文檔約束:DTD  Document Type Definition
                   :標籤,屬性,層級,前後順序
 -->
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 數據庫鏈接相關 -->
        <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
        <property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="hibernate.connection.username">hr</property>
        <property name="hibernate.connection.password">hr</property>
        <!-- 最大鏈接數 -->
        <property name="hibernate.c3p0.max_size">3</property>
        <!-- 最下鏈接數 -->
        <property name="hibernate.c3p0.min_size">1</property>
        <!-- 獲取連接等待超時時間: 毫秒-->
        <property name="checkoutTimeout">3000</property>
        <!-- 指示鏈接池的類型 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <!-- hibernate自身配置信息 
                    方言:指示數據庫種類,便於hibernate對不一樣的數據庫作出相應的適應。
        -->
        <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
        <!-- 控制檯打印出sql語句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 格式化sql語句 -->
        <property name="hibernate.format_sql">true</property>
        <!-- 禁用掉javaEE6的bean-validator -->
        <property name="javax.persistence.validation.mode">none</property>
        <!-- getCurrentSession -->
        <property name="hibernate.current_session_context_class">thread</property>
        <!-- 開啓二級緩存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- 二級緩存類別:EhCache,OSCache,JbossCache -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <!-- 開啓查詢緩存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- 映射信息的註冊 -->
        <mapping class="com.c50.entity.User"></mapping>
    </session-factory>
</hibernate-configuration>

測試代碼:

package com.c50.test;

import java.util.Iterator;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.c50.entity.User;
import com.c50.util.HibernateUtil;

/**
 * 
 * @author Administrator
 * 緩存測試
 */
public class TestCache {

    /**
     * 一級緩存測試:不能跨事務
     */
    @Test
    public void testFirstLevelCache(){
        Session session=HibernateUtil.getCurrentSession();
        Transaction tx=session.beginTransaction();
        //1.檢查緩存,沒有可用的數據,轉向數據庫,查詢出的數據,進入一級緩存
        User user=(User)session.get(User.class,1);
        System.out.println(user);
        user.setAge(41);
        //2.再次發起一樣的查詢,檢查緩存,有可用的數據,則不在查詢數據庫。
        User user2=(User)session.get(User.class,1);
        System.out.println(user2);
        tx.commit();
        
        /*Session session2=HibernateUtil.openSession();
        Transaction tx2=session2.beginTransaction();
        User user3=(User)session2.get(User.class,109);
        System.out.println(user3);
        tx2.commit();
        session2.close();*/
    }
    /**
     * 一級緩存測試:HQL語句執行查詢不能檢查緩存
     */
    @Test
    public void testFirstLevelCache2(){
        Session session=HibernateUtil.getCurrentSession();
        Transaction tx=session.beginTransaction();
        //1.檢查緩存,沒有可用的數據,轉向數據庫,查詢出的數據,進入一級緩存
        User user=(User)session.get(User.class,1);
        //2.list和uniqueResult不能檢查緩存,直接轉向數據庫。
        String hql="from User u where u.name='zhangjifeng'";
        Query query=session.createQuery(hql);
        User user2=(User)query.uniqueResult();
        System.out.println(user);
        System.out.println(user2);
        tx.commit();
    }
    /**
     * 一級緩存測試:iterate():
     */
    @Test
    public void testFirstLevelCache3(){
        Session session=HibernateUtil.getCurrentSession();
        Transaction tx=session.beginTransaction();
        //1.HQL查詢多條數據,數據進入一級緩存
        String hql="from User u where u.name like ?";
        Query query=session.createQuery(hql);
        query.setString(0,"ji%");
        query.list();
        
        String hql2="from User u where u.name like ?";
        Query query2=session.createQuery(hql);
        query2.setString(0,"ji%");
        //迭代器中存儲ID
        //只查詢ID,經過ID檢查緩存。
        Iterator it=query2.iterate();
        while(it.hasNext()){
            User user=(User)it.next();
            System.out.println(user);
        }
        tx.commit();
    }
    /**
     * 二級緩存測試
     */
    @Test
    public void testSecondLevelCache(){
        Session session=HibernateUtil.openSession();
        Transaction tx=session.beginTransaction();
        User user=(User)session.get(User.class,1);
        User user3=(User)session.get(User.class,2);
        System.out.println(user);
        tx.commit();
        session.close();
        
        Session session2=HibernateUtil.openSession();
        Transaction tx2=session2.beginTransaction();
        
        User user2=(User)session2.get(User.class,1);
        user2.setName("second222");
        System.out.println(user2);
        tx2.commit();
        session2.close();
    }
    /**
     * 查詢緩存測試
     */
    @Test
    public void testQueryCache(){
        Session session=HibernateUtil.openSession();
        Transaction tx=session.beginTransaction();
        String hql="select u.name,u.age from User u where u.age>=?";
        Query query=session.createQuery(hql);
        query.setInteger(0,20);
        //本次查詢要使用查詢緩存
        query.setCacheable(true);
        //檢查查詢緩存,沒有可用的緩存,則轉向數據庫,將結果緩存在查詢緩存中:{HQL:結果(字段)}
        query.list();
        tx.commit();
         session.close();
        
//         Session session3=HibernateUtil.openSession();
//        Transaction tx3=session3.beginTransaction();
//         session3.save(new User());
//        tx3.commit();
//        session3.close();
        
        Session session2=HibernateUtil.openSession();
        Transaction tx2=session2.beginTransaction();
        String hql2="select u.name,u.age from User u where u.age>=?";
        Query query2=session2.createQuery(hql2);
        query2.setInteger(0,20);
        //本次查詢要使用查詢緩存
        query2.setCacheable(true);
        //檢查查詢緩存,發現可用緩存數據,直接使用,不在查詢數據庫
        query2.list();
        tx2.commit();
        session2.close();
    }
    /**
     * 查詢緩存+二級緩存
     */
    @Test
    public void testQueryCache2(){
        Session session=HibernateUtil.openSession();
        Transaction tx=session.beginTransaction();
        String hql="from User u where u.age>=?";
        Query query=session.createQuery(hql);
        query.setInteger(0,20);
        //本次查詢要使用查詢緩存
        query.setCacheable(true);
        //檢查查詢緩存,沒有可用數據,則轉向數據庫,得到一個實體User,將{HQL:User的ID}存入查詢緩存
        //                                              將{User的ID:User}存入二級緩存
        query.list();
        tx.commit();
         session.close();
        
        
        Session session2=HibernateUtil.openSession();
        Transaction tx2=session2.beginTransaction();
        String hql2="from User u where u.age>=?";
        Query query2=session2.createQuery(hql2);
        query2.setInteger(0,20);
        //本次查詢要使用查詢緩存
        query2.setCacheable(true);
        //檢查查詢緩存,得到緩存數據:User的ID,經過UserID檢查二級緩存,若是有數據,則直接使用,不然
        //以各個ID爲條件分別發起查詢
        query2.list();
        tx2.commit();
        session2.close();
    }
    
}
相關文章
相關標籤/搜索