热门标签 | 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里面的第三点。


推荐阅读
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 利用决策树预测NBA比赛胜负的Python数据挖掘实践
    本文通过使用2013-14赛季NBA赛程与结果数据集以及2013年NBA排名数据,结合《Python数据挖掘入门与实践》一书中的方法,展示如何应用决策树算法进行比赛胜负预测。我们将详细讲解数据预处理、特征工程及模型评估等关键步骤。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 本文介绍了如何在多线程环境中实现异步任务的事务控制,确保任务执行的一致性和可靠性。通过使用计数器和异常标记字段,系统能够准确判断所有异步线程的执行结果,并根据结果决定是否回滚或提交事务。 ... [详细]
  • 本文介绍 SQL Server 的基本概念和操作,涵盖系统数据库、常用数据类型、表的创建及增删改查等基础操作。通过实例帮助读者快速上手 SQL Server 数据库管理。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • 社交网络中的级联行为 ... [详细]
  • 本文深入探讨了Memcached的内存管理机制,特别是其采用的Slab Allocator技术。该技术通过预分配不同大小的内存块来有效解决内存碎片问题,并确保高效的数据存储与检索。文中详细描述了Slab Allocator的工作原理、内存分配流程以及相关的优化策略。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 在 Android 开发中,通过 Intent 启动 Activity 或 Service 时,可以使用 putExtra 方法传递数据。接收方可以通过 getIntent().getExtras() 获取这些数据。本文将介绍如何使用 RoboGuice 框架简化这一过程,特别是 @InjectExtra 注解的使用。 ... [详细]
  • 本文详细介绍了 Java 中 org.geotools.data.shapefile.ShapefileDataStore 类的 getCurrentTypeName() 方法,并提供了多个代码示例,帮助开发者更好地理解和使用该方法。 ... [详细]
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社区 版权所有