`

框架学习之Hibernate 第九节 缓存原理与分析

 
阅读更多

1. 一级缓存:Session中共享

测试:可以通过查看输出的 select 语句的数目来测试Hibernate中的Session级的缓存

使用缓存的三种操作:放,取,删

会从缓存中拿数据的方法:get,load,iterate   [id为native(自增长的形式的话,save方法是不会放入到一级缓存或者二级缓存中的)]

会向缓存中放入数据的方法:save,update,saveOrUpdate,get,load,iterate,lock(还没有使用过,lock是把一个没有更改过的脱管状态的对象变成持久状态)

删除缓存内容的方法:evict(清除某个数据),clear(清除所有  s.clear()  清除一级缓存中的内容)

缺点:Hibernate的缓存内容默认是不会清除的,所以,如果数据内容太大时会造成缓存溢出!

还有一个缺点是:作用范围比较小,只在session关闭之前有效

模拟缓存实现:Map

package cn.itcast.hibernate;

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

import cn.itcast.hibernate.domain.User;

public class CacheDemo {

    static Map cache = new HashMap();
    
    public static void main(String[] args) {
        User u = getUser(1);//第一次是从数据库中取出来
        User u1 = getUser(1);//第二次是直接从缓存中取数据
    }

    public static void update(User user){
        updateDB(user);
        String key = User.class.getName() + user.getId();
        cache.remove(key);
    }

    public static User getUser(int id) {
        String key = User.class.getName() + id;
        User user = (User) cache.get(key);
        if (user != null)
            return user;
        user = getFromDB();
        cache.put(key, user);
        return user;
    }
    
    private static void updateDB(User user) {
        // TODO Auto-generated method stub
    }
    private static User getFromDB() {
        // TODO Auto-generated method stub
        return null;
    }
}

 

 

2. 二级缓存:SessionFactory级共享

实现为可插拔,通过修改cache.provider_class参数来改变;

hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持,可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。

Hibernate默认的二级缓存支持类:hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider  采用的是Hashtable,但是性能并不是很好

如果要设置某个类的对象存入缓存中有两种方法:

①在hibernate.cfg.xml中加入:

<class-cache class="className" usage="read-only"/>

②在映射文件的class元素加入子元素:

<cache usage="read-write"/>

其中usage:read-only,read-write,nonstrict-read-write,transactional

 

配置

1.开启二级缓存

2.设置二级缓存的第三方类

3.导入相应的包并配置

4.设置允许放入缓存的类

例如:

hibernate.cfg.xml

        <!-- 开启二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <!-- 开启查询语句可以使用二级缓存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
        <!-- 设置二级缓存的支持类 -->
        <property name="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
        <!-- 设置可以放入缓存中的类 -->
        <class-cache usage="read-only" class="com.yinger.domain.User"/>

 

添加 文件 oscache.properties (一般是在Hibernate下载到的etc文件夹中),可以自行设置里面的属性值

 

统计信息:可以得到很多的关于二级缓存的信息(例如:hit,put,miss次数)

用sessionFactory.getSatistics()获取统计信息

示例代码:

Statistics st = HibernateUtils.getSessionFactory().getStatistics();

System.out.println(st.getSecondLevelCachePutCount());

System.out.println(st.getSecondLevelCacheHitCount());

System.out.println(st.getSecondLevelCacheMissCount());

 

 

从二级缓存取数据的方法:get,load,iterator,而存数据的方法很多

Session的save(这个方法不适合native生成方式的主键),update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,

但(在没打开查询缓存时)只有Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询,如果没有取到数据的话)。

Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;修改cache.use_query_cache为true打开对查询的缓存,

并且调用query.setCacheable(true)或criteria.setCacheable(true)。

测试代码: N+1次查询的问题的代码
/**
 * @Author:胡家威  
 * @CreateTime:2011-8-16 上午12:52:21
 * @Description:
 */

package com.yinger.main;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.stat.Statistics;

import com.yinger.domain.IdCard;
import com.yinger.domain.Person;
import com.yinger.util.HibernateUtils;

public class One2One {

    public static void main(String[] args) {
        add();
        add();
        System.out.println("-------------");
        ncpp();
        System.out.println("-------------");
        iterate();
        
//        Statistics st = HibernateUtils.getSessionFactory().getStatistics();
//        System.out.println(st.getSecondLevelCachePutCount());
//        System.out.println(st.getSecondLevelCacheHitCount());
//        System.out.println(st.getSecondLevelCacheMissCount());
        
//        IdCard card = queryIdCardById(1);
//        System.out.println(card.getPerson().getName());
    }

    // 添加信息到数据库中
    private static void add() {
        Person p = new Person();
        p.setName("person name");

        IdCard card = new IdCard();
        card.setLife(new Date());

        // p.setIdCard(card); // 外键一对一映射的这种情况下 id_card表中的person_id字段没有值
        card.setPerson(p);

        Session s = null;
        Transaction tr = null;
        try {
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(p);
            s.save(card);

            tr.commit();
        } catch (Exception e) {
            if (tr != null)
                tr.rollback();
        } finally {
            if (s != null)
                s.close();
        }
    }

    // 根据id查询IdCard
    public static IdCard queryIdCardById(int id) {
        IdCard card = null;
        Session s = null;
        try {
            s = HibernateUtils.getSession();
            card = (IdCard) s.get(IdCard.class, id);
            Hibernate.initialize(card.getPerson()); // 可以使用这个方法来初始化代理对象
//            System.out.println(card.getPerson().getName());
            return card;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (s != null) {
                s.close();
            }
        }
        return card;
    }
    
    
    // 测试 N+1 次查询
    @SuppressWarnings("unchecked")
    private static void ncpp() {
        Session s = HibernateUtils.getSession();
        Query q = s.createQuery("from IdCard");
        q.setCacheable(true);
        List<IdCard> ics = q.list();
        for (IdCard ic : ics) {
            System.out.println(ic.getPerson().getName());
        }
        s.close();
    }
    
    // 测试 N+1 次查询
    @SuppressWarnings("unchecked")
    private static void iterate() {
        Session s = HibernateUtils.getSession();
        Query q = s.createQuery("from IdCard");
        q.setCacheable(true);
        Iterator<IdCard> it = q.iterate();
        while(it.hasNext()) {
            System.out.println(it.next().getPerson().getName());
        }
        s.close();
    }

}

 

如果在hibernate.cfg.xml中配置了

<!-- 设置可以放入缓存中的类 -->
<class-cache usage="read-only" class="com.yinger.domain.Person"/>
<class-cache usage="read-only" class="com.yinger.domain.IdCard"/>

 

测试结果:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (life) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (life) values (?)
-------------
Hibernate: select idcard0_.id as id, idcard0_.life as life4_ from id_card idcard0_
Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=?
person name
Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=?
person name
-------------
Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_
person name
person name

 

 

如果调换一下 ncpp 和 iterate 方法的调用顺序,结果是:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (life) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (life) values (?)
-------------
Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_
Hibernate: select idcard0_.id as id0_, idcard0_.life as life4_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=?
person name
Hibernate: select idcard0_.id as id0_, idcard0_.life as life4_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=?
person name
-------------
Hibernate: select idcard0_.id as id, idcard0_.life as life4_ from id_card idcard0_
person name
person name

 

很清楚的发现两次查询实现了SessionFactory级别(也就是二级缓存)的共享,iterate方法共需要执行5个select,ncpp需要调用3个select

如果没有在hibernate配置文件中加上那两句,是不行的,默认这两个类是不会放入二级缓存的,结果总是3+5=8条select语句

[不过奇怪的是我删掉了q.setCacheable(true);语句之后结果不变,按理应该不会存入到二级缓存中的啊]

 

 

 

3.其他的缓存

分布式缓存(每台服务器保存自己的缓存):如果数据更新了,并且需要更新的其他服务器很多,那么广播出去进行更新的话就很耗时 [读取次数大于更新次数]

中央缓存(利用一台服务器来进行缓存):读取数据时要从另一台服务器得到,这个比较耗时

使用缓存的条件

1.读取大于修改。

2.数据量不能超过内存容量。

3.对数据要有独享的控制。

4.可以容忍出现无效数据。

 

 

 

优秀的文章推荐:

Hibernate3一级缓存和二级缓存的理解!


Hiberante3
一级缓存总结

1. Session 级别的缓存,它同session邦定。它的生命周期和session相同。Session消毁,它也同时消毁;管理一级缓存,一级缓存无法取消,用两个方法管理,clear(),evict()

2. 两个session 不能共享一级缓存,因它会伴随session的生命周期的创建和消毁;

3. Session缓存是实体级别的缓存,就是只有在查询对象级别的时候才使用,如果

使用HQL和SQL是查询属性级别的,是不使用一级缓存的!

4 . iterate 查询使用缓存,会发出查询Id的SQL和HQL语句,但不会发出查实体的,

它查询完会把相应的实体放到缓存里边,一些实体查询如果缓存里边有,就从缓存中查询,但还是会发出查询id的SQL和HQL语句。如果缓存中没有它会数据库中查询,然后将查询到的实体一个一个放到缓存中去,所以会有N+1问题出现。

5 . List()和iterate 查询区别:

使用iterate,list查询实体对象

*N+1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题

所谓的N+1是在查询的时候发出了N+1条sql语句1:首先发出一条查询对象id列表的sqlN:

根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句

list和iterate的区别?

list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据

iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题

6.get()和load(),iterate方法都会使用一级缓存

7.hiberate3 session 存储对象的过程如下:

例如 object 对象

       Session.save(object);

这时候不会把数据放到数据库,会先放到session缓存中去,数据库中没有相应记录,session.flush();才发SQL和HQL语句,数据库中有了相应记录,

但是数据库用select查不到,这是跟数据库事务级别有关系


Hiberante3 二级缓存总结

1.Hibernate3的(sessionFactory)二级缓存和session级别的缓存一样都只对实体对象做缓存,不对属性级别的查询做缓存;二级缓存的生命周期和sessionFactory的生命周期是一样的,sessionFactory可以管理二级缓存;

2.sessionFactory级别的缓存,需要手动配置;所有的session可以共享sessionFactory 级别的缓存;(一般把一些不经常变化的实体对象放到sessionFactory级别的缓存中

3.Hiberante3二级缓存的配置和使用方法如下:

1. 必须把ehcache.jar包导入,然后到Hibernate3.2的etc文件下把ehcache.xml复制到工程src目录下(ehcache.xml里边的参数里边有详细英文说明);

(说明:ehcache.jar是第三方法的缓存产品,hiberante只是把它做了集成,还有好多第三方hibernate集成的缓存产品,相关说明请查阅hiberante3开发手册;ehcache支持分布应用的(这个和Hibernate3.2开发手册有出入,经过官网查证确实支持了),如果有分布式需求,请换成支持分布式的二级缓存产品,hiberate3开发手册都有相头说明。配置方法都类似);

4.Hibernate3的二级缓存默认是开起的,也可以指定开起。在hibernate.cfg.xml 文件下配置如下:

*修改hibernate.cfg.xml文件,开启二级缓存;

<property name=”hibernate.cache.use_second_level_cache”>true</property>

*指定二级缓存产品的提供商;

<property name=”hibernate.cache.provider_class”> org.hibernate.cache.EhCacheProvider

</property>

要让那些实体使用二级缓存,在hibernate.cfg.xml配置文件中加入:

<!—

让这个实体用二级缓存 也可以在实体中映射文件去配置即:

<cache usage="read-only"/>

-->

<class-cache class=”com.zzz.hibernate.ClassT” usage=”read-only”/>

Read-only一般使用这个策略,其它的hibernate3开发手册中也有详细介绍;

CacheMode去hibernate3开发手册中搜索这个关键字,可以找到一级缓存和二级缓存交互使用的问题;

 

 

分享到:
评论

相关推荐

    Hibernate开源框架学习

    9. **缓存机制**:Hibernate提供了第一级缓存(Session级别的)和第二级缓存(SessionFactory级别的),能有效提高数据访问性能。 10. **关联映射**:Hibernate支持一对一、一对多、多对一和多对多等多种关联关系...

    hibernate学习资料大全

    理解ORM的概念是学习Hibernate的第一步。 2. **Hibernate配置**: 在使用Hibernate时,我们需要配置一个名为`hibernate.cfg.xml`的文件,其中包含了数据库连接信息、方言、缓存策略等。学习如何正确配置这些参数...

    hibernate框架学习实例

    **hibernate框架学习实例** Hibernate 是一个强大的Java持久化框架,它简化了数据库与对象之间的交互,使得开发者可以更加专注于业务逻辑而不必过多地处理SQL语句。本实例是针对hibernate 3.x版本设计的教学案例,...

    Hibernate框架

    7. **第一级缓存与第二级缓存**:Hibernate的第一级缓存是每个会话的私有缓存,存储了会话中所有实体的最新状态。第二级缓存则是一个可选特性,允许多个会话共享数据,提高了性能,但需要谨慎使用以避免并发问题。 ...

    Hibernate缓存

    【标题】:“Hibernate缓存”涉及的是Java持久化框架Hibernate中的一个重要特性,即数据缓存。缓存的主要目的是提高数据访问速度,减少数据库的负担,从而提升应用性能。Hibernate提供了两级缓存机制:第一级缓存是...

    Hibernate原理解析

    `FrameTest`可能包含了实现这些功能的测试代码,可以用来验证和学习Hibernate的各种特性。阅读和分析源代码是深入理解Hibernate原理的有效途径。 总之,Hibernate通过ORM简化了数据库操作,提升了开发效率,而对其...

    java框架Hibernate详细PDF教学

    9. **缓存机制**:探讨Hibernate的一级缓存(Session级别)和二级缓存(SessionFactory级别),以及第三方缓存插件如Ehcache的集成和使用。 10. **实体状态与生命周期**:了解Hibernate中对象的四种状态(瞬时、...

    自己动手模仿Hibernate写数据库框架

    9. **性能优化**:学习Hibernate如何处理批处理、连接池等性能优化策略,并在我们的框架中应用。 10. **测试与文档**:创建单元测试以验证框架的功能,同时编写清晰的文档,帮助其他开发者理解和使用我们的框架。 ...

    Hibernate DOC中文文档(学习Hibernate必备手册)

    《Hibernate DOC中文文档》是学习Hibernate框架的重要参考资料,它详细阐述了Hibernate的核心概念、配置、对象关系映射(ORM)以及各种操作技巧。对于初学者和有经验的开发者来说,这份文档都是掌握Hibernate不可或...

    hibernate最简单的例子,未整合其他框架

    这个压缩包文件“hibernate最简单的例子”提供了一个不涉及其他框架的纯净Hibernate应用实例,非常适合初学者学习和理解Hibernate的基本用法。 1. Hibernate简介: Hibernate是基于Java的开源ORM框架,它通过XML...

    hibernate框架官方4.4.0版本下载

    3. **更好的第二级缓存集成**:Hibernate 4.4.0增强了对第三方缓存提供者(如Ehcache、Infinispan等)的支持,提供了更好的二级缓存配置和使用体验,有助于减少数据库负载,提高系统响应速度。 4. ** Criteria API...

    Hibernate框架教学课件

    在学习Hibernate之前,需要了解ORM的基本概念。ORM允许开发者使用面向对象的编程方式来操作数据库,而无需直接编写SQL语句。Hibernate作为ORM框架,将Java类映射到数据库表,使得数据操作变得简单易行。 课程内容...

    java框架hibernate

    Java框架Hibernate是一个...学习Hibernate框架,不仅可以提升数据库操作的效率,还能帮助开发者更好地理解和实践面向对象的设计原则。结合实际项目练习,可以深入理解其工作原理和最佳实践,从而在软件开发中游刃有余。

    hibernate第一个hibernate

    《Hibernate入门:初识与实践》 ...总之,"hibernate第一个hibernate"项目是一个绝佳的起点,它将引导你了解并掌握Hibernate的基本概念和操作。通过实践,你可以逐步熟悉ORM思想,为后续的Java开发奠定坚实的基础。

    Hibernate学习.pdf

    - **Ehcache.jar:** 第三方缓存组件,用于实现Hibernate的一级缓存和二级缓存。 - **Hibernate3.jar:** Hibernate核心库,包含所有主要的Hibernate功能。 - **Jta.jar:** Java Transaction API,用于处理事务。 - **...

    精通Hibernate:Java持久化对象技术详解[第二版]

    《精通Hibernate:Java持久化对象技术详解[第二版]》是一部深入探讨Hibernate框架的专业书籍,旨在帮助Java开发者熟练掌握和运用这一强大的ORM(Object-Relational Mapping)工具。Hibernate是Java开发领域中广泛...

    hibernate aip hibernate aip

    6. **缓存策略**:Hibernate提供了第一级缓存(Session级别的)和第二级缓存(SessionFactory级别的)。合理利用缓存能提高性能,但需注意缓存一致性问题。 7. **性能优化**:包括延迟加载(Lazy Loading)、批处理...

    hibernate的第一个例子

    **标题解析:**“hibernate的第一个例子”表明这是一个关于Hibernate框架的基础教程,主要目标是展示如何使用Hibernate进行数据持久化操作。 **描述分析:**描述提到这是一个超级简单的例子,包含一个持久化对象...

    Hibernate4的一些总结

    7. **缓存机制**:解释Hibernate的两级缓存(第一级缓存Session和第二级缓存Provider),以及查询缓存的工作原理和配置。 8. **延迟加载与立即加载**:分析Hibernate4的懒加载和急加载策略,以及如何通过注解或配置...

Global site tag (gtag.js) - Google Analytics