热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)

本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。

eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存

  • 1、前言
  • 2、项目实践
    • 2.1 jar包拉取
    • 2.2 数据库结构
    • 2.3 创建配置文件hibernate.cfg.xml
    • 2.4 创建ehcache.xml
    • 2.5 创建Teacher类
    • 2.6 Teacher.hbm.xml
    • 2.7 创建测试CacheLearn
  • 3、1级2级缓存相关概念以及缓存策略
    • 3.1一级缓存
    • 3.2 二级缓存
    • 3.3 缓存的并发策略
    • 3.4 缓存提供者
  • 4、说到2级缓存,那就要说1+n问题(这个了解一下就好了)
    • 4.1 list方法的缓存
  • 5、说到缓存,就不得不说get和load方法的区别
    • 5.1 对于Hibernate的get方法。
    • 5.2 对于Hibernate的load方法。
    • 5.3 说完它们的不同工作流程,那么来说一下它们如果出错会报什么异常吧
  • 项目地址


1、前言

这个我在操作的过程中遇到了很多知识点,不只是一篇文章了,我都看了一下然后外加自己研究操作做了一个实践纪录。

2、项目实践

2.1 jar包拉取

在这里插入图片描述

2.2 数据库结构

在这里插入图片描述
在这里插入图片描述

SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (`id` int(11) NOT NULL,`name` varchar(255) NOT NULL,`subject` varchar(255) NOT NULL,`favorite_food` varchar(255) NOT NULL,`height` double(30,0) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('2', 'li', 'chinese', 'apple', '168');
INSERT INTO `teacher` VALUES ('3', '移动', '电子科技', '钢铁', '1000000');
INSERT INTO `teacher` VALUES ('10', '老王', '英文', '桃子', '175');

2.3 创建配置文件hibernate.cfg.xml

hibernate.cfg.xml,这里主要看缓存那里即可。这里的mapping我把class注释是为了先不使用注释的方法,如果使用注释的方法实现2级缓存也可以,不过到时候类上要加入@Cache注解


"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/student?serverTimezone=UTC
root
root
10
true
org.hibernate.dialect.MySQLDialect




updatethreadtrue
true
true
ehcache


2.4 创建ehcache.xml

ehcache.xml,这个文件可以不写,不写就是使用我这里的default缓存。
这里可能会有人不理解为什么空闲时间跟生存时间不一致,为什么要设置这两个?因为你很有可能生存了9分钟,然后被访问了一次,空闲时间重置了,这个时候就不需要挂掉被删掉,还是要缓存。同时成立才要被删除这种设计挺不错的。






-->


2.5 创建Teacher类

Teacher,
@org.hibernate.annotations.Cache标注的3个属性
usage,设置二级缓存的并发策略,并发策略后面单拎出来说
region,设置二级缓存的区域,自定义的cache名称
include,设置是否缓存lazy的数据,all表示缓存lazy的数据,non-lazy表示只缓存非lazy的数据

package com.czx.cache.pojo;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;@Entity
@Table(name = "teacher")
//@Cache( usage=CacheConcurrencyStrategy.READ_WRITE,region="sampleCache1")
public class Teacher {@Id@Column(name = "id")private Integer id;@Column(name = "name")private String name;@Column(name = "subject")private String subject;@Column(name = "favorite_food")private String favoriteFood;@Column(name = "height")private Double height;public Teacher(Integer id, String name, String subject, String favoriteFood, Double height) {super();this.id = id;this.name = name;this.subject = subject;this.favoriteFood = favoriteFood;this.height = height;}public Teacher() {super();}@Overridepublic String toString() {return "Teacher [id=" + id + ", name=" + name + ", subject=" + subject + ", favoriteFood=" + favoriteFood+ ", height=" + height + "]";}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getFavoriteFood() {return favoriteFood;}public void setFavoriteFood(String favoriteFood) {this.favoriteFood = favoriteFood;}public Double getHeight() {return height;}public void setHeight(Double height) {this.height = height;}}

2.6 Teacher.hbm.xml

Teacher.hbm.xml,
cache 的3个属性
usage,设置二级缓存的并发策略,并发策略后面单拎出来说
region,设置二级缓存的区域,自定义的cache名称
include,设置是否缓存lazy的数据,all表示缓存lazy的数据,non-lazy表示只缓存非lazy的数据


一个teacher类的描述处理

2.7 创建测试CacheLearn

CacheLearn,这里我加入线程休眠是为了测试2级缓存是否过期,没啥问题的,自己注释打开就能得到不一样的结果了

package com.czx.cache;import java.util.Date;import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;import com.czx.cache.pojo.Teacher;public class CacheLearn {
private static SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();public static void main(String[] args) {CacheLearn cacheLearn = new CacheLearn();cacheLearn.cache007();}public void cache007() {Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();Teacher t = session.load(Teacher.class, 3);System.out.println(t.getFavoriteFood());Thread tt =new Thread();transaction.commit();
// System.out.println(new Date());//弄个线程处理休眠下,这里毫秒单位来的
// try {
// tt.sleep(60000L);
// }catch (Exception e) {
// // TODO: handle exception
// }
// System.out.println(new Date());Session session2 = sessionFactory.openSession();Transaction transaction2 = session2.beginTransaction();Teacher t2 = session2.load(Teacher.class, 3);System.out.println(t2);transaction2.commit();session.close();}
}

3、1级2级缓存相关概念以及缓存策略

下面有个图我们客户端通过session进行查询操作,这个时候我们会先去找一级缓存有没有,如果有就直接返回,如果没有就去找2级缓存有没有,如果有也可以直接返回给客户端。都没有的情况下去找数据库做相关操作,操作完成数据存到session1级缓存中去,然后在存到2级缓存去,最后返回给客户端。

在这里插入图片描述

3.1一级缓存

第一级缓存是 Session 缓存并且是一种强制性的缓存,所有的要求都必须通过它。Session 对象在它自己的权利之下,在将它提交给数据库之前保存一个对象。

如果你对一个对象发出多个更新,Hibernate 会尝试尽可能长地延迟更新来减少发出的 SQL 更新语句的数目。如果你关闭 session,所有缓存的对象丢失,或是存留,或是在数据库中被更新。

3.2 二级缓存

第二级缓存是一种可选择的缓存并且第一级缓存在任何想要在第二级缓存中找到一个对象前将总是被询问。第二级缓存可以在每一个类和每一个集合的基础上被安装,并且它主要负责跨会话缓存对象。

3.3 缓存的并发策略

并发策略
一个并发策略是一个中介,它负责保存缓存中的数据项和从缓存中检索它们。如果你将使用一个二级缓存,你必须决定,对于每一个持久类和集合,使用哪一个并发策略。
缓存并发策略,可以得知这里面的级别是从高到低的,Transactional最高最严谨,阻止脏读不可重复读,遇到下面就越不严谨,根据项目的具体情况去选择合适的。

名称含义
Transactional(事务型)仅在受管理环境下适用.它提供了RepeatableRead事务隔离级别.对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读
Read-write(读写型)提供ReadCommited数据隔离级别.对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读
Nonstrict-read-write(非严格读写)不保证缓存与数据库中数据的一致性.提供ReadUncommited事务隔离级别,对于极少被修改,而且允许脏读的数据,可以采用这种策略
Read-only(只读型)提供Serializable数据隔离级别,对于从来不会被修改的数据,可以采用这种访问策略

3.4 缓存提供者

下面这个其实主要看他能否集群,支持什么集群就够了。

缓存名称描述
EHCache它能在内存或硬盘上缓存并且集群缓存,而且它支持可选的 Hibernate 查询结果缓存。
OSCache支持在一个单独的 JVM 中缓存到内存和硬盘,同时有丰富的过期策略和查询缓存支持。
warmCache一个基于 JGroups 的聚集缓存。它使用集群失效但是不支持 Hibernate 查询缓存。
JBoss Cache一个也基于 JGroups 多播库的完全事务性的复制集群缓存。它支持复制或者失效,同步或异步通信,乐观和悲观锁定。Hibernate 查询缓存被支持。

下面这个的话是不同的缓存提供者支持什么类型的缓存策略。每一个缓存提供者都不和每个并发策略兼容。以下的兼容性矩阵将帮助你选择一个合适的组合。

策略/提供者Read-onlyNonstrictread-writeRead-writeTransactional
EHCacheXXX
OSCacheXXX
SwarmCacheXX
JBoss CacheXX

4、说到2级缓存,那就要说1+n问题(这个了解一下就好了)

我先说一下1+n是什么问题吧。
网络上有的说是如果你查询一个集合,但是呢,由于你查询的只是一个集合,返回的是一个id集合并没有真的去细查,到了真正要使用的时候,又会再去查一次数据库。
举例说明:
如果你使用的是iterate这个方法,那么就会出现这个问题,结果如下,你可以看看。(这个iterate已经过期了,了解一下看看吧)

Iterator iterator = session.createQuery("from Teacher").iterate();for(;iterator.hasNext();) {System.out.println(iterator.next());}

在这里插入图片描述但是,当你使用查询的list方法不会有1+n问题,所以我感觉没啥用,而且iterate方法也已经过期。这个1+n问题也可以通过2级缓存解决。

4.1 list方法的缓存

list() 默认情况只会放入缓存,不会从一级缓存中取!
使用查询缓存,可以让list()查询从二级缓存中取!
1、开启查询缓存后查询的list()可以从二级缓存中拿数据(list()不能从一级缓存[session缓存]中拿数据),但是不主动去拿,需要设置setCacheable(true)

5、说到缓存,就不得不说get和load方法的区别

get和load都是通过id获取到对应的数据。
关于这个get说法众说纷纭,各有各的看法,我自己操作看看才能知道结果。百度有的说get可以查2级,有人说不行,我自己测试不就知道了。
在我配置好了2级缓存的情况下,执行以下代码(以下是方法,调用一下即可)
这里我一开始使用get获取数据,关闭session,在获取一次数据看看,结果如下,显而易见,这个get是可以查2级缓存的。

public void cacheTest() {Session session = sessionFactory.openSession();Transaction transaction = session.beginTransaction();Teacher t = session.get(Teacher.class, 3);System.out.println(t);transaction.commit();session.close();Session session2 = sessionFactory.openSession();Transaction transaction2 = session2.beginTransaction();Teacher t2 = session2.get(Teacher.class, 3);System.out.println(t2);transaction2.commit();session2.close();}

在这里插入图片描述

5.1 对于Hibernate的get方法。

这里说下其实本质就是如果要查数据,先查看一下session一级缓存有没有数据,如果没有就去2级缓存中找,都没有再去查找数据库。其中每一个session是独立开来的,如果你关闭了session,那么里面的缓存也会丢失,但是2级缓存的介质不一样所以还能存起来,所以如果加入了2级缓存那么只要查一次就算清空session之后也还能查到数据。
这里有一个特例,如果数据库没有数据,那么就没法缓存,每一次去查都要去到数据库查为null的数据,这样就会造成缓存穿透。
看到这里如果你对上面概念还是不了解在建议看看下面我举得例子。
第一次查数据的情况:
首先启动session---->
然后去session缓存(一级缓存)中找。第一次是没有的---->
所以去更下级缓存中找,2级缓存有没有呢?其实还能继续往下找更高级的缓存,但是这里以2级缓存为例就够了。---->
缓存中找不到了,那么查数据库去,如果没有对应的数据,那么也会返回null,正常有数据就能返回对应的对象。然后将数据缓存到session缓存中。


连续二次查数据的情况下(没有关闭session,在一个session中查数据,这里2级缓存用不到):
第一次查询结束---->
接着查相同的hql/sql语句---->
去session缓存中找,找到了---->
返回对应的对象即可


连读二次查数据的情况(在不同的session)
第一次查询数据结束---->
关闭session---->
新建一个新的sessionA---->
使用sessionA查数据---->
sessionA缓存中没有数据,去查找数据库---->
将数据库数据返回


连读二次查数据的情况(在不同的session,启用了2级缓存)
第一次查询数据结束---->
关闭session–>
新建一个新的sessionA---->
使用sessionA查数据----->
sessionA中没有数据,去2级缓存中找,找到了---->
将2级缓存中内容返回

5.2 对于Hibernate的load方法。

如果你使用了get方法返回数据那么必定会有是数据库的查询语句,但是如果你使用的是load方法,那么就未必是了。
当我们使用session.load()方法来加载一个对象时,此时并不会发出SQL语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象(例如要打印这个数据),得到其它属性时,这个时候才会发出SQL语句,从数据库中去查询对象。如果你只是要获取id值,那么也并不会访问数据库。
实际上工作流程是这样的,这类似懒加载,我们一开始加载数据都去缓存中找,先一级后2级,找不到了,那么也先会返回一个代理对象,里面只带有对象id。到了我们要使用该对象(不是id的其他属性)的时候,那么才会真正去向数据库发请求,然后将数据缓存到session中去。第二次查找如果缓存没了那么跟第一次查找一样。缓存还在进行第二次查找,加载数据去缓存中找,返回一个对象的引用,到了要使用该对象的时候可以直接从缓存中获取了

第一次查找数据
使用load方法获取数据----->
去缓存中找数据,没有就返回一个代理对象---->
该代理对象带id,如果你要使用该对象除了id以外的属性那么就要去查询数据库------>
数据库获取到了数据,返回并将数据存到session缓存中去

第二次查找数据,缓存在
使用load方法获取数据------>
去缓存中找数据,发现有数据,于是就返回该对象的引用----->
然后获取属性可以去缓存中拿了。

5.3 说完它们的不同工作流程,那么来说一下它们如果出错会报什么异常吧

上面流程说过了,get是会从数据库获取对象的,那么如果数据库没有这个对象,我还要调用这个对象的属性,那么会报什么异常呢?java.lang.NullPointerException,空指针异常,是null的。
load方法会返回一个代理对象,那么如果数据库没有,我还要调用这个对象的属性会报什么异常?org.hibernate.ObjectNotFoundException,因为我们的代理对象只有id,其他属性都是没有的呀。

项目地址

这篇不是很建议看项目,因为我在里面不停的做不同情况下的测试,不过如果你想要看,那也行吧。
https://gitee.com/mrchen13427566118/ssh_hibernate_learn.git里面的ssh_hibernate_cache。
打开方式可以看这篇
https://blog.csdn.net/weixin_43987277/article/details/116936221里面的第三点。


推荐阅读
  • SQLite数据库CRUD操作实例分析与应用
    本文通过分析和实例演示了SQLite数据库中的CRUD(创建、读取、更新和删除)操作,详细介绍了如何在Java环境中使用Person实体类进行数据库操作。文章首先阐述了SQLite数据库的基本概念及其在移动应用开发中的重要性,然后通过具体的代码示例,逐步展示了如何实现对Person实体类的增删改查功能。此外,还讨论了常见错误及其解决方法,为开发者提供了实用的参考和指导。 ... [详细]
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • Spring框架中的面向切面编程(AOP)技术详解
    面向切面编程(AOP)是Spring框架中的关键技术之一,它通过将横切关注点从业务逻辑中分离出来,实现了代码的模块化和重用。AOP的核心思想是将程序运行过程中需要多次处理的功能(如日志记录、事务管理等)封装成独立的模块,即切面,并在特定的连接点(如方法调用)动态地应用这些切面。这种方式不仅提高了代码的可维护性和可读性,还简化了业务逻辑的实现。Spring AOP利用代理机制,在不修改原有代码的基础上,实现了对目标对象的增强。 ... [详细]
  • 在过去,我曾使用过自建MySQL服务器中的MyISAM和InnoDB存储引擎(也曾尝试过Memory引擎)。今年初,我开始转向阿里云的关系型数据库服务,并深入研究了其高效的压缩存储引擎TokuDB。TokuDB在数据压缩和处理大规模数据集方面表现出色,显著提升了存储效率和查询性能。通过实际应用,我发现TokuDB不仅能够有效减少存储成本,还能显著提高数据处理速度,特别适用于高并发和大数据量的场景。 ... [详细]
  • Java Web开发中的JSP:三大指令、九大隐式对象与动作标签详解
    在Java Web开发中,JSP(Java Server Pages)是一种重要的技术,用于构建动态网页。本文详细介绍了JSP的三大指令、九大隐式对象以及动作标签。三大指令包括页面指令、包含指令和标签库指令,它们分别用于设置页面属性、引入其他文件和定义自定义标签。九大隐式对象则涵盖了请求、响应、会话、应用上下文等关键组件,为开发者提供了便捷的操作接口。动作标签则通过预定义的动作来简化页面逻辑,提高开发效率。这些内容对于理解和掌握JSP技术具有重要意义。 ... [详细]
  • 本文深入探讨了CGLIB BeanCopier在Bean对象复制中的应用及其优化技巧。相较于Spring的BeanUtils和Apache的BeanUtils,CGLIB BeanCopier在性能上具有显著优势。通过详细分析其内部机制和使用场景,本文提供了多种优化方法,帮助开发者在实际项目中更高效地利用这一工具。此外,文章还讨论了CGLIB BeanCopier在复杂对象结构和大规模数据处理中的表现,为读者提供了实用的参考和建议。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • 本文详细介绍了 jQuery 的入门知识与实战应用,首先讲解了如何引入 jQuery 库及入口函数的使用方法,为初学者提供了清晰的操作指南。此外,还深入探讨了 jQuery 在实际项目中的多种应用场景,包括 DOM 操作、事件处理和 AJAX 请求等,帮助读者全面掌握 jQuery 的核心功能与技巧。 ... [详细]
  • 本文介绍了MySQL中一些基本但重要的数学函数,包括角度与弧度之间的转换函数RADIANS(X)和DEGREES(X),以及正弦函数。RADIANS(X)用于将角度值转换为弧度值,而DEGREES(X)则将弧度值转换为角度值。这些函数在处理涉及角度和弧度的计算时非常有用,能够简化复杂的数学运算。此外,正弦函数在三角学和工程计算中也具有广泛的应用,能够帮助用户更高效地进行数据处理和分析。 ... [详细]
  • CentOS 7环境下Jenkins的安装与前后端应用部署详解
    CentOS 7环境下Jenkins的安装与前后端应用部署详解 ... [详细]
  • 基址获取与驱动开发:内核中提取ntoskrnl模块的基地址方法解析
    基址获取与驱动开发:内核中提取ntoskrnl模块的基地址方法解析 ... [详细]
  • 在搭建Hadoop集群以处理大规模数据存储和频繁读取需求的过程中,经常会遇到各种配置难题。本文总结了作者在实际部署中遇到的典型问题,并提供了详细的解决方案,帮助读者避免常见的配置陷阱。通过这些经验分享,希望读者能够更加顺利地完成Hadoop集群的搭建和配置。 ... [详细]
  • 《精通 jQuery》第六章:深入解析与实战应用
    《精通 jQuery》第六章:深入解析与实战应用本章详细探讨了 Ajax 技术的核心机制及其实际应用。Ajax 通过 XMLHttpRequest 对象实现客户端与服务器之间的异步数据交换,从而在不重新加载整个页面的情况下更新部分内容。这种技术不仅提升了用户体验,还提高了应用的响应速度和效率。此外,本章还介绍了如何利用 jQuery 简化 Ajax 操作,并提供了多个实战案例,帮助读者更好地理解和掌握这一重要技术。 ... [详细]
author-avatar
zhj808008
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有