hibernate

在开发工作的时候遇到了一个bug,看了半天才发现是自己的逻辑写的有问题,但在找bug途中怀疑过时hibernate缓存的问题,所以去网上看了资料学了一下

N+1问题第一种

前提:Hibernate默认表与表的关联方法是fetch="select"
多对一关系中,如果一个班级对应多个学生,加载班级的时候n个学生也要加载进来,加载n个学生时还需要多加载一个班级信息
这样有两个缺点
1)访问数据库数据太多,select语句执行过多,频繁访问数据库会影响检索性能,其实可以直接用join语句完成,没必要这样
2)在应用逻辑只需要访问班级信息时,加载学生信息就很多余,而且浪费内存空间。
为了解决以上问题,Hibernate提供了两种检索策略:延迟检索策略和迫切左外连接检索策略
1.延迟检索策略
2.迫切左外连接检索策略

N+1问题第二种

我们看到,当如果通过iterator()方法来获得我们对象的时候,hibernate首先会发出1条sql去查询出所有对象的 id 值,当我们如果需要查询到某个对象的具体信息的时候,hibernate此时会根据查询出来的 id 值再发sql语句去从数据库中查询对象的信息,这就是典型的 N+1 的问题。
那么这种 N+1 问题我们如何解决呢,其实我们只需要使用 list() 方法来获得对象即可。但是既然可以通过 list() 我们就不会出现 N+1的问题,那么我们为什么还要保留 iterator()这种形式呢?我们考虑这样一种情况,如果我们需要在一个session当中要两次查询出很多对象,此时我们如果写两条 list()时,hibernate此时会发出两条 sql 语句,而且这两条语句是一样的,但是我们如果第一条语句使用 list(),而第二条语句使用 iterator()的话,此时我们也会发两条sql语句,但是第二条语句只会将查询出对象的id,所以相对应取出所有的对象而已,显然这样可以节省内存,而如果再要获取对象的时候,因为第一条语句已经将对象都查询出来了,此时会将对象保存到session的一级缓存中去,所以再次查询时,就会首先去缓存中查找,如果找到,则不发sql语句了。

缓存问题

一级缓存(session缓存):默认使用的缓存
当从数据库中查询了数据之后,会将数据存放到一级缓存中,如果下一次需要获取相同的数据时,不会再次查询数据库,而是直接从缓存中获取数据

一级缓存的生命周期和Session相同,意味着生命期很短,以及缓存无法取消
evict()方法,将某个对象从Session的一级缓存中清除
clean()方法:将一级缓存中的所有对象全部清除
Query.list()方法不存在缓存问题,需要重新查询数据库
Hiobernate的二级缓存:每个Session共用的缓存,全局缓存。

二级缓存 (sessionFactory) : 内部提供二级缓存接口

特点: 和session对象没有关系(session是否关闭 对二级缓存无影响)
基于Hibernate的JPA二级缓存是sessionFactory负责管理的。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与Hibernate集成。第二级缓存是可选的,可以在每个类或每个集合的粒度上配置第二级缓存。

我们通常使用二级缓存都是将其配置成 read-only ,即我们应当在那些不需要进行修改的实体类上使用二级缓存,否则如果对缓存进行读写的话,性能会变差,这样设置缓存就失去了意义。

二级缓存的仅仅是对象,如果你只是提取一些属性,就不会保存到二级缓存中,例如只查所有学生的姓名。

二级缓存也不会缓存hql语句

二级缓存提供的清除方法为:
按对象class清空缓存
按对象class和对象的主键id清空缓存
清空对象的集合中的缓存数据等

适用情况:

1、数据不会被第三方修改;
2、数据大小在可接收范围之内;
3、数据更新频率低;
4、非关键数据(不是财务数据等)

三种情况绕开hibernate

1.多个应用系统同时访问一个数据库
此种情况使用hibernate二级缓存会不可避免的造成数据不一致的问题,此时要进行详细的设计。比如在设计上避免对同一数据表的同时的写入操作,
使用数据库各种级别的锁定机制等。

2、动态表相关(可以去看一下)
所谓“动态表”是指在系统运行时根据用户的操作系统自动建立的数据表。

比如“自定义表单”等属于用户自定义扩展开发性质的功能模块,因为此时数据表是运行时建立的,所以不能进行hibernate的映射。因此对它的操作只能是绕开hibernate的直接数据库JDBC操作。
如果此时动态表中的数据没有设计缓存,就不存在数据不一致的问题。
如果此时自行设计了缓存机制,则调用自己的缓存同步方法即可。

3、使用sql对hibernate持久化对象表进行批量删除时
此时执行批量删除后,缓存中会存在已被删除的数据。

hibernate中的持久化对象 (三种状态):

transient(瞬时态):通过new关键字直接获取;尚未与Session关联对象,失去引用的话,就会被JVM回收。一般就是直接New创建的对象。

persistent(持久态):通过get/load、Query查询获得;已经与当前session产生关联,并且相关联的session没有关闭,并且事务尚未提交。

detached(脱管态):无法直接获得;存在持久化OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

持久化对象之间的转变:

瞬时——持久:save、saveOrUpdate(都是通过session获得)

瞬时——脱管:对象.setID(1);为瞬时对象设置新的OID

持久——瞬时:delete(被删除持久化对象,不建议再次使用)

持久——脱管:evict(清除一级缓存中某个对象)、close(关闭Session,清除一级缓存)、clear(清除一级缓存所有对象)

脱管——瞬时:对象.setID(null);删除对象OID

脱管——持久:update、saveOrUpdate、lock

参考:
[1]https://www.cnblogs.com/xiaoluo501395377/p/3377604.html
[2]https://blog.csdn.net/weixin_42249629/article/details/81840715

你可能感兴趣的:(hibernate)