一:为什么使用Hibernate缓存:

  Hibernate是一个持久层框架,经常访问物理数据库。

  为了降低应用程序访问物理数据库的频次,从而提高应用程序的性能。

  缓存内的数据是对物理数据源的复制,应用程序在运行时从缓存中读取数据,在特定时间或事件会同步缓存和物理数据源的数据

二:什么是Hibernate缓存:

  Hibernate缓存分为两种:一级缓存,二级缓存。

    1.一级缓存:又称为Session缓存,

    Session缓存是Hibernate内置的缓存,不能被卸载,生命周期也就是在open和close之间,也就是每打开一个Session,内存中就会创建一块Session缓存区。

    Session缓存中,每个持久化类都拥有一个唯一的OID。(在Hibernate中,类对象分为四种状态:持久化,游离,临时,销毁)。

      ①.对于刚创建的一个对象,如果session中和数据库中都不存在该对象,那么该对象就是临时

      对象(Transient)

    ②.临时对象调用save方法,或者游离对象调用update方法可以使该对象变成持久化对象,如果
      对象是持久化对象时,那么对该对象的任何修改,都会在提交事务时才会与之进行比较,
      如果不同,则发送一条update语句,否则就不会发送语句

    ③.游离对象就是,数据库存在该对象,但是该对象又没有被session所托管

    ④.销毁状态顾名思义,就是被Delete的数据,只有临时状态和游离状态才能转换为销毁状态

    通过OID查询到数据都会存放在Session缓存(一级缓存)中。

    2.二级缓存:又称为SessionFactory缓存。

    SessionFactory缓存是用户可选的,默认情况下不会开启,可以选择不同的缓存提供商来进行配置。

    SessionFactory缓存的声明周期是在应用程序运行到程序结束之间,就是说每一个程序只会拥有一个SessionFactory缓存,因为二级缓存是在进程范围或者

    说集群范围,所以有可能出现并发问题,因此需要采用适当的并发缓存策略,该策略为被缓存的数据提供了事务隔离级别。

    Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。

    什么样的数据适合存放在二级缓存中

    1>不会经常被修改的数据

    2>常量数据

    3>不是很重要的数据,允许偶尔出现并发的数据

    4>不能被并发访问的数据,例如:银行账户

    

    3.延迟加载

    延迟加载是在用户查询某个实体时,加载这个实体的同时,并不会加载该实体所关联的其它对象,而是会产生一个代理对象,在真正使用到这个对象的时候,

    才会通过在缓存中存放的该对象的OID去查询数据库,并返回查询结果,如果查询条件是除了主键OID外,都会直接去查询数据库,通过延迟加载也是大大

    提高了应用程序的运行效率。

    

    4.应用程序查询数据

    在应用程序通过OID查询数据的时候,会先从一级缓存中查询,如果查不到就会从二级缓存中查询,都查不到才会从数据库中查找。

一级缓存和二级缓存的对比:

  一级缓存 二级缓存
存放数据的形式 相互关联的持久化对象 对象的散装数据
缓存的范围

由于每个事务都拥有单独的一级缓存

不会出现并发问题,因此无须提供并发访问策略

由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别

数据过期策略

处于一级缓存中的对象永远不会过期,除非应用程

序显示清空或者清空特定对象

必须提供数据过期策略,如基于内存的缓存中对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间

物理介质 内存

内存和硬盘,对象的散装数据首先存放到基于内存的缓存中,当内存中对象的数目达到数据过期策略的maxElementsInMemory值,就会把其余的对象写入基于硬盘的缓存中

缓存软件实现 在Hibernate的Session的实现中包含

由第三方提供,Hibernate仅提供了缓存适配器,用于把特定的缓存插件集成到Hibernate中

启用缓存方式

只要通过Session接口来执行保存,更新,删除,

加载,查询,Hibernate就会启用一级缓存,对

于批量操作,如不希望启用一级缓存,直接通过

JDBCAPI来执行

用户可以再单个类或类的单个集合的粒度上配置第二级缓存,如果类的实例被经常读,但很少被修改,就可以考虑使用二级缓存,只有为某个类或集合配置了二级缓存,Hibernate在运行时才会把它的实例加入到二级缓存中

用户管理缓存的方式

一级缓存的物理介质为内存,由于内存的容量有限

,必须通过恰当的检索策略和检索方式来限制加载对象的数目,Session的evit()方法可以显示的清空缓存中特定对象,但不推荐

二级缓存的物理介质可以使内存和硬盘,因此第二级缓存可以存放大容量的数据,数据过期策略的maxElementsInMemory属性可以控制内存中的对象数目,管理二级缓存主要包括两个方面:选择

需要使用第二级缓存的持久化类,设置合适的并发访问策略;选择缓存适配器,设置合适的数据过期策略。SessionFactory的evit()方法也可以显示的清空缓存中特定对象,但不推荐

并发访问策略 由于每个事务都拥有单独的一级缓存不会出现并发问题,因此无须提供并发访问策略

由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别

    

    

    

接下来我们通过例子来讲解一下缓存的具体使用

  1. 一级缓存的讲解

  >>一级缓存主要分为两种:使用HQL语句操作和不使用HQL操作,两者差别主要在于是否是通过OID来操作物理数据库

  先贴上代码:添加一个对象实体,以下Session对象都已经通过SessionFactory静态获取

public void addUser(){
Session session = null;
Transaction tran = null;
try{
session = sf.openSession(); // 创建一个Session
tran = session.beginTransaction(); //开启事务 User user = new User();
user.setName("王五");
session.save(user);
User user2 = (User) session.get(User.class, 4);
System.out.println(user2); tran.commit();//事务提交
}catch(Exception e){
tran.rollback(); //事务回滚
throw(e);
}finally{
session.close(); //关闭session
}
}

  结果:

    Hibernate: insert into user_1 (name) values (?)
    User [id=4, name=王五]

  结果很明显,数据库只有插入的操作,并没有查询的操作,之前有讲过,只要是通过OID操作的数据,都会保存在Session缓存中(即一级缓存),

  既然缓存中有这条数据,那就没必要在数据库中找了

  获得一个实体对象:获得对象Session中有get()和load()两个方法,load()方法比较特殊,会有延迟加载的特效(延迟加载在上面有讲)

@Test
public void getUser(){
Session session = null;
Transaction tran = null;
try{
session = sf.openSession(); // 创建一个Session
tran = session.beginTransaction(); //开启事务 /*这里指明你要获得哪个类型,Hibernate会根据类名查询映射配置文件到数据库查询哪张表,根据指定
* id查询实体,通过反射机制创建实体对象
*/
User user1 = (User) session.get(User.class, 1); //执行查询,get
System.out.println(user1);
User user2 = (User) session.get(User.class, 1);
System.out.println(user2); tran.commit();//事务提交
}catch(Exception e){
tran.rollback(); //事务回滚
throw(e);
}finally{
session.close(); //关闭session
}
}

  >>结果:

    Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from user_1 user0_ where user0_.id=?
    User [id=1, name=李四]
    User [id=1, name=李四]

  从结果可以看出:session通过OID查询只查询了一次,因为在第一次查询查询到的数据已经存放在了Session缓存中(一级缓存)中了,所以再次

          获得该实体对象,应用程序会先查询Session缓存,既然查询到了,就不会再到数据库中查找,所以这里只有一条查询语句。

  

  ----以上的代码,我在获取两个User中间加上一行代码:

user1.setName("测试_2");

  结果:

    Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from user_1 user0_ where user0_.id=?
    User [id=1, name=测试_1]
    User [id=1, name=测试_2]
    Hibernate: update user_1 set name=? where id=?

  从结果看出:在更新一条数据的时候,并不会立即去数据库进行更新操作,而是先更新Session缓存中的数据,在事务提交

        或Session关闭的时候应用程序会发现Session缓存中的数据有改变,然后才进行更新数据库操作,在Session类中还有一个

        flush()这个方法,和IO流相似,立即刷新数据到目的地,也就是立即把数据更新到数据库中,在这里我并没有使用。

  更新操作和删除操作是一样的,就不多说了。。

  >>使用Hibernate的HQL操作数据

  HQL和SQL基本上一样的,区别:SQL是针对数据库表查询,而HQL是针对类查询;SQL不区分大小写,HQL对类名区分大小写。

  使用HQL操作数据会把查询到的数据保存在缓存区中,但是不会从缓存中查找,而是直接到数据库查找

    接下来使用HQL操作一下

List<User> list1 = session.createQuery("FROM User WHERE id=3").list();
System.out.println(list1);
List<User> list2 = session.createQuery("FROM User WHERE id=3").list();
System.out.println(list2);

  结果:     

    Hibernate: select user0_.id as id0_, user0_.name as name0_ from user_1 user0_ where user0_.id=3
    [User [id=3, name=张三]]
    Hibernate: select user0_.id as id0_, user0_.name as name0_ from user_1 user0_ where user0_.id=3
    [User [id=3, name=张三]]

  结果可以看出:这里进行了两次查询,说明第一次查询到的数据并没有进缓存,即使限定了id。

  使用迭代器来查询

Iterator<User> it1 = session.createQuery("FROM User").iterate();
while(it1.hasNext()){
System.out.println(it1.next());
}
Iterator<User> it2 = session.createQuery("FROM User").iterate();
while(it2.hasNext()){
System.out.println(it2.next());
}

  结果:

    Hibernate: select user0_.id as col_0_0_ from user_1 user0_
    Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from user_1 user0_ where user0_.id=?
    User [id=2, name=张三]
    Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from user_1 user0_ where user0_.id=?
    User [id=3, name=张三]
    Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from user_1 user0_ where user0_.id=?
    User [id=4, name=王五]
    Hibernate: select user0_.id as col_0_0_ from user_1 user0_
    User [id=2, name=张三]
    User [id=3, name=张三]
    User [id=4, name=王五]

  这里第一次是先查询到所有User的id,然后再根据id去查到所有实体对象,使用迭代器能够迫使程序能够通过id去查询

  ,只有通过OID操作的数据才会进缓存,尽管如此,通过这种方式提高的效率还是有限的,还是会产生大量的查询语句。

  2. 二级缓存

    二级缓存在Hibernate中默认是关闭的,需要在Hibernate.hbm.xml中配置开启,并配置缓存的提供商,除此之外还

    要配置需要添加到缓存的类或集合(class-cache | collection-cache)

  在hibernate.cfg.xml中的配置

<!-- 默认情况下二级缓存是关闭的 -->
<!-- 选择使用二级缓存的提供商 -->
<property name="cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 默认是false,这里选择的值是 查询缓存 -->
<property name="cache.use_query_cache">true</property>

 前面我们使用HQL进行查询获得一个List集合,虽然也能缓存,但是又有局限性,接下来我们使用二级缓存进行查询

  

  虽然配置了二级缓存,但是并没有指定要缓存的类,所以还要添加缓存类的配置,具体usage的值可以查询API文档

<!-- 在这里要指定缓存类的全限定名 -->
<class-cache usage="read-write" class="com.a_helloworld.User"/>
Session session1  = sf.openSession(); // 创建一个Session
Transaction tran1 = session1.beginTransaction(); //开启事务 List<User> list1 = session1.createQuery("FROM User WHERE id<10")
.setCacheable(true)
.list();
System.out.println(list1); tran1.commit();//事务提交
session1.close(); //关闭session

     //第二个Session
Session session2 = sf.openSession(); // 创建一个Session
Transaction tran2 = session2.beginTransaction(); //开启事务 List<User> list2 = session2.createQuery("FROM User WHERE id<10")
.setCacheable(true)
.list();
System.out.println(list2); tran2.commit();//事务提交
session2.close(); //关闭session

  结果: 

    Hibernate: select user0_.id as id0_, user0_.name as name0_ from user_1 user0_ where user0_.id<10
    [User [id=2, name=张三], User [id=3, name=张三], User [id=4, name=测试2], User [id=5, name=呵呵]]
    [User [id=2, name=张三], User [id=3, name=张三], User [id=4, name=测试2], User [id=5, name=呵呵]]

    结果看出只有一条查询语句,第二次查询并没有通过数据库查询,而是从缓存区直接拿到了数据。但是,如果

      把第二次查询的条件修改一下,就需要从数据库查询,说明这里存储的只是HQL语句。

接下来就不贴代码了,说一下我自己经过测试的问题,大家可以自己亲身测试一下。

   >>只要是经过配置的类,所有查询到的数据都会更新二级缓存中

   >>进行更新或者删除操作,程序会通知缓存进行更新

   >>现在很晚了,一点多了,有时间再改进吧。。。

如需转载,请说明出处:http://www.cnblogs.com/gudu1/p/6882155.html

  

  

  

Hibernate学习笔记二:Hibernate缓存策略详解的更多相关文章

  1. Redis学习笔记--Redis数据过期策略详解

    本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...

  2. Redis学习笔记--Redis数据过期策略详解==转

    本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存sessi ...

  3. K8S学习笔记之Kubernetes 部署策略详解

    0x00 概述 在Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了. 选择正确的部署策略是要依赖于我们的业务需求的,下面我们 ...

  4. 小程序学习笔记二:页面文件详解之 .json文件

       页面配置文件—— pageName.json 每一个小程序页面可以使用.json文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json 的 window 中相同的配置项. 页面的 ...

  5. IP地址和子网划分学习笔记之《IP地址详解》

    2018-05-03 18:47:37   在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. ...

  6. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  7. Hibernate学习笔记二

    Hibernate持久化类的编写规则 Hibernate是持久层的ORM映射框架,专注于数据的持久化工作.所谓持久化,就是将内存中的数据永久存储到关系型数据库中. 持久化类 一个java类与数据库表建 ...

  8. Hibernate学习笔记(六) — Hibernate的二级缓存

    我们知道hibernate的一级缓存是将数据缓存到了session中从而降低与数据库的交互.那么二级缓存呢? 一.应用场合 比方.在12306购票时.须要选择出发地与目的地,假设每点一次都与数据库交互 ...

  9. Hibernate:Hibernate缓存策略详解

    一:为什么使用Hibernate缓存: Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序访问物理数据库的频次,从而提高应用程序的性能. 缓存内的数据是对物理数据源的复制,应用 ...

随机推荐

  1. 性能调优案例分享:Mysql的cpu过高

    性能调优案例分享:Mysql的cpu过高   问题:一个系统,Mysql数据库,数据量变大之后.mysql的cpu占用率很高,一个测试端访问服务器时mysql的cpu占用率为15% ,6个测试端连服务 ...

  2. js全选checkbox框

    html: <input  type="checkbox" id="checkbox1" value="1" onclick=&quo ...

  3. Hibernate(三)之配置文件详解

    一.核心配置文件(hibernate.cfg.xml) <?xml version="1.0" encoding="UTF-8"?> <!DO ...

  4. Android IPC机制全解析<一>

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

  5. Java 基础知识总结

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.数据类型:  数据类型:1>.基本数据类型:1).数值型: 1}.整型类型(byte  8位   (by ...

  6. 设置ZooKeeper服务器地址列表源码解析及扩展

    设置ZooKeeper服务器地址列表源码解析及扩展 ZooKeeper zooKeeper = new ZooKeeper("192.168.109.130:2181",SESSI ...

  7. C字符串处理函数

    部分参考百科. C常用字符串函数:字符串输入函数,字符串输出函数,字符串处理函数,标准输入输出流 字符串处理函数: 1.字符串长度:strlen(str),返回字符串实际长度,不包括'\0',返回值类 ...

  8. Unity C# 多态 委托 事件 匿名委托 Lambda表达式 观察者模式 .NET 框架中的委托和事件

    一.多态 里氏替换原则: 任何能用基类的地方,可以用子类代替,反过来不行.子类能够在基类的基础上增加新的行为.面向对象设计的基本原则之一. 开放封闭原则: 对扩展开放,意味着有新的需求或变化时,可以对 ...

  9. 业务订单号生成算法,每秒50W左右,不同机器保证不重复,包含日期可读性好

    参考snowflace算法,基本思路: 序列12位(更格式化的输出后,性能损耗导致每毫秒生成不了这么多,所以可以考虑减少这里的位,不过留着也并无影响) 机器位10位 毫秒为左移 22位 上述几个做或运 ...

  10. Spring+SpringMVC+MyBatis+easyUI整合优化篇(十二)数据层优化-explain关键字及慢sql优化

    本文提要 从编码角度来优化数据层的话,我首先会去查一下项目中运行的sql语句,定位到瓶颈是否出现在这里,首先去优化sql语句,而慢sql就是其中的主要优化对象,对于慢sql,顾名思义就是花费较多执行时 ...