Hibernate之二級緩存


一:二級緩存簡介

為什么需要緩存:

      查詢大量數據時更快,拉高程序的性能

什么樣的數據需要緩存:

      很少被修改或根本不改的數據 數據字典

  業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等

關系型數據庫:

     數據與數據之間存在關系(聯系)的數據庫 mysql/Oracle、sqlserver

非關系型數據庫(數據與數據之間是不存在關系的,key-value):

  •   基於文件存儲的數據庫:ehcache
  •        基於內存存儲的數據庫:redis、memcache
  •        基於文檔存儲的數據庫:mongodb

ehcache是什么:

       Ehcache 是現在最流行的純Java開源緩存框架,配置簡單、結構清晰、功能強大

  我們來用map簡單測試一下緩存原理:

package com.ht.six.test;

import java.util.HashMap;
import java.util.Map;

/**
 * 利用map集合簡易實現緩存原理
 * @author Administrator
 *
 */
public class EhcacheDemo1 {
    static Map<String, Object> cache = new HashMap<String, Object>();
    static Object getValue(String key) {
        Object value = cache.get(key);
        System.out.println("從緩存中獲取數據。。。。");
        if(value == null) {
            System.out.println("從軟件相對應的配置文件中獲取數據。。。");
            cache.put(key, new String[] {"zs"});
            return cache.get(key);
        }
        return value;
    }
    
    /**
     * 1.從緩存中獲取數據。。。。
     * 2.從軟件相對應的配置文件中獲取數據。。。
     * 3."zs"
     * 4.從緩存中獲取數據。。。。
     * 5."zs"
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(getValue("sname"));
        System.out.println(getValue("sname"));
    }
}

運行原理圖解:

我們來看一下運行結果:

 

 ehcache簡介

ehcache特點:

  • 夠快

     Ehcache的發行有一段時長了,經過幾年的努力和不計其數的性能測試,Ehcache終被設計於large, high concurrency systems.

  • 夠簡單

    開發者提供的接口非常簡單明了,從Ehcache的搭建到運用運行僅僅需要的是你寶貴的幾分鍾。其實很多開發者都不知道自己用在用Ehcache,Ehcache被廣泛的運用於其他的開源項目

  • 夠小

    關於這點的特性,官方給了一個很可愛的名字small foot print ,一般Ehcache的發布版本不會到2M,V 2.2.3 才 668KB。

  • 夠輕量

    核心程序僅僅依賴slf4j這一個包,沒有之一!

  • 好擴展

    Ehcache提供了對大數據的內存和硬盤的存儲,最近版本允許多實例、保存對象高靈活性、提供LRU、LFU、FIFO淘汰算法,基礎屬性支持熱配置、支持的插件多

  • 監聽器

    緩存管理器監聽器 (CacheManagerListener)和 緩存監聽器(CacheEvenListener),做一些統計或數據一致性廣播挺好用的

  • 分布式緩存

    從Ehcache 1.2開始,支持高性能的分布式緩存,兼具靈活性和擴展性

核心接口:

  1. CacheManager:緩存管理器
  2. Cache:緩存對象,緩存管理器內可以放置若干cache,存放數據的實質,所有cache都實現了Ehcache接口
  3. Element:單條緩存數據的組成單位

ehcache的使用

    首先我們在POM.XML中導入相關依賴

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <servlet.version>4.0.0</servlet.version>
        <hibernate.version>5.2.12.Final</hibernate.version>
        <mysql.driver.version>5.1.46</mysql.driver.version>
    <ehcache.version>2.10.0</ehcache.version>
        <slf4j-api.version>1.7.7</slf4j-api.version>
        <log4j-api.version>2.9.1</log4j-api.version>
    </properties>
  <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>${ehcache.version}</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <!-- slf4j核心包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j-api.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!--用於與slf4j保持橋接 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j-api.version}</version>
        </dependency>
        <!--核心log4j2jar包 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j-api.version}</version>
        </dependency>
  </dependencies>

用二級緩存來演示存儲數據

    導入相關配置文件

  ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--磁盤存儲:將緩存中暫時不使用的對象,轉移到硬盤,類似於Windows系統的虛擬內存-->
    <!--path:指定在硬盤上存儲對象的路徑-->
    <!--java.io.tmpdir 是默認的臨時文件路徑。 可以通過如下方式打印出具體的文件路徑 System.out.println(System.getProperty("java.io.tmpdir"));-->
    <diskStore path="D://xxx"/>


    <!--defaultCache:默認的管理策略-->
    <!--eternal:設定緩存的elements是否永遠不過期。如果為true,則緩存的數據始終有效,如果為false那么還要根據timeToIdleSeconds,timeToLiveSeconds判斷-->
    <!--maxElementsInMemory:在內存中緩存的element的最大數目-->
    <!--overflowToDisk:如果內存中數據超過內存限制,是否要緩存到磁盤上-->
    <!--diskPersistent:是否在磁盤上持久化。指重啟jvm后,數據是否有效。默認為false-->
    <!--timeToIdleSeconds:對象空閑時間(單位:秒),指對象在多長時間沒有被訪問就會失效。只對eternal為false的有效。默認值0,表示一直可以訪問-->
    <!--timeToLiveSeconds:對象存活時間(單位:秒),指對象從創建到失效所需要的時間。只對eternal為false的有效。默認值0,表示一直可以訪問-->
    <!--memoryStoreEvictionPolicy:緩存的3 種清空策略-->
    <!--FIFO:first in first out (先進先出)-->
    <!--LFU:Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存-->
    <!--LRU:Least Recently Used(最近最少使用). (ehcache 默認值).緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存-->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
</ehcache>

  EhcacheUtil類

package com.ht.six.util;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import java.io.InputStream;

public class EhcacheUtil {

    private static CacheManager cacheManager;

    static {
        try {
            InputStream is = EhcacheUtil.class.getResourceAsStream("/ehcache.xml");
            cacheManager = CacheManager.create(is);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private EhcacheUtil() {
    }

    public static void put(String cacheName, Object key, Object value) {
        Cache cache = cacheManager.getCache(cacheName);
        if (null == cache) {
            //以默認配置添加一個名叫cacheName的Cache
            cacheManager.addCache(cacheName);
            cache = cacheManager.getCache(cacheName);
        }
        cache.put(new Element(key, value));
    }


    public static Object get(String cacheName, Object key) {
        Cache cache = cacheManager.getCache(cacheName);
        Element element = cache.get(key);
        return null == element ? null : element.getValue();
    }

    public static void remove(String cacheName, Object key) {
        Cache cache = cacheManager.getCache(cacheName);
        cache.remove(key);
    }
}

測試類:

package com.ht.six.test;

import com.ht.six.util.EhcacheUtil;

/**
 * 演示利用緩存存儲數據
 * @author Administrator
 *
 */
public class EhcacheDemo2 {
    public static void main(String[] args) {
        System.out.println(System.getProperty("java.io.tmpdir"));
        EhcacheUtil.put("com.ht.four.entity.Book", 11, "zhangsan");
        System.out.println(EhcacheUtil.get("com.ht.four.entity.Book", 11));
    }
}

若是自己沒有去配置槽中的內容,管理器一般就是自動規划為默認配置,不會存儲到硬盤里面去

什么是槽?由下面的圖告訴你cache的內部結構

 

把槽中數據修改一下測試一下

<!--name: Cache的名稱,必須是唯一的(ehcache會把這個cache放到HashMap里)-->
    <cache name="com.ht.one.entity.User" eternal="false" maxElementsInMemory="100"
           overflowToDisk="true" diskPersistent="true" timeToIdleSeconds="0"
           timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>

測試結果中,我們在D盤中發現存儲進去的文件:

 

hibernate中使用二級緩存(ehcache)

  • 依賴包已經在上面全部導入 所以這里就不重復了

  hibernate一級緩存使用

  

/**
     * 默認情況下,sql語句形成了三次,這里為了提高性能,必須使用二級緩存SessionFactory緩存
     *  <!-- 開啟二級緩存 -->
     *  <property name="hibernate.cache.use_second_level_cache">true</property>
      <!-- 開啟查詢緩存 -->
      <property name="hibernate.cache.use_query_cache">true</property>
      <!-- EhCache驅動 -->
      <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
      
          映射文件中添加標簽
          <cache usage="read-write" region="com.javaxl.one.entity.User"/>
          這里的region指的是Ehcache.xml中cacheName
     * @param args
     */
    public static void main(String[] args) {
        UserDao userDao  = new UserDao();
        User u = new User();
        u.setId(7);
        User user = userDao.get(u);
        System.out.println(user);
        User user2 = userDao.get(u);
        System.out.println(user2);
        User user3 = userDao.get(u);
        System.out.println(user3);
        
    }

因為一個session中只生產一個sql語句,所以這里用了多個session,要是小數據還可行,大量數據就大大降低性能,那么就要用到二級緩存了。

  • hibernate.cfg.xml中添加二級緩存相關配置

     <!-- 開啟二級緩存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- 開啟查詢緩存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- EhCache驅動 -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhCacheRegionFactory</property>
  • 對應的映射文件中添加緩存配置 User.hbm.xml

        <!-- 添加緩存標號 指定槽 -->
<cache usage="read-write" region="com.ht.one.entity.User"/>
  • 調用 測試方法

/**
     * 同一個session,sql語句只生成一次,這里用到了一級緩存
     * 
     * session級別的緩存就是一級緩存
     * sessionfactory級別的緩存就是二級緩存
     * 默認的一級緩存開啟的
     */
    public static void test1() {
        Session session = SessionFactoryUtils.openSession();
        Transaction transaction = session.beginTransaction();
        
        User user = session.get(User.class, 7);
        System.out.println(user);
        User user2 = session.get(User.class, 7);
        System.out.println(user2);
        User user3 = session.get(User.class, 7);
        System.out.println(user3);
        
        transaction.commit();
        session.close();
    }

EhcacheDemo4

package com.ht.six.test;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;

import com.ht.two.util.SessionFactoryUtils;


/**
 * hibernate二級緩存不會同時緩存多條數據
 * @author Administrator
 *
 */
public class EhcacheDemo4 {
    public static void main(String[] args) {
        Session session = SessionFactoryUtils.openSession();
        Transaction transaction = session.beginTransaction();
        
        Query query = session.createQuery("from User");
        query.setCacheable(true);
        List list = query.list();
        System.out.println(list);
        List list2 = query.list();
        System.out.println(list2);
        List list3 = query.list();
        System.out.println(list3);
        
        
        transaction.commit();
        session.close();
    }
}

發現他也執行多次

  原因:對於查詢單條數據的時候,緩存機制是默認用一級緩存的,若是想要用二級緩存來提高性能,還得靠自己手動開啟二級緩存

query.setCacheable(true);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM