首页 Java java教程 详解Java的Hibernate框架中的缓存与二级缓存

详解Java的Hibernate框架中的缓存与二级缓存

Jan 23, 2017 am 09:57 AM

缓存

今天我们就来讲一下hibernate中实体状态和hibernate缓存。
 1)首先我们先来看一下实体状态:
 实体状态主要分三种:transient,persitent,detached。
 看英文应该就大概明白了吧。
 transient:是指数据还没跟数据库中的数据相对应。
 persistent:是指数据跟数据库中的数据相对应,它的任何改变都会反映到数据库中。
 detached:是指数据跟数据库中的数据相对应,但由于session被关闭,它所做的修改不会对数据库的记录造成影响。
 下面我们直接代码来:

Transaction tx = session.beginTransaction(); 
User user = new User(); 
user.setName("shun"); 
//这里的user还未保存到数据库,数据库表中并没有与之对应的记录,它为transient状态 
session.save(user); 
tx.commit(); 
//提交之后user变为persistent状态 
session.close(); 
//由于session关闭,此时的user为detached状态,它的所有修改都不会反映到数据库中。 
      
Session session2 = sessionFactory.openSession(); 
tx = session2.beginTransaction(); 
user.setName("shun123"); 
session2.saveOrUpdate(user); 
tx.commit(); 
//当我们调用了saveOrUpdate之后,user重新变为persistent状态,它的所有修改都会反映到数据库中。 
session2.close();
登录后复制

我们看到代码,首先我们定义了一个对象user,在未保存之前,它就是transient状态,在数据库中并没有与它相应的记录。而当我们进行保存并提交修改后,user成为persistent状态,在数据库中有相应的一条记录。而当我们把session关闭后,user就变成了detached状态了,它的更改并不会反映到数据库中,除非我们手动调用saveOrUpdate等相应的更新和添加方法。而当我们直接想让它从persistent到transient状态,怎么办呢?直接删除就可以了,删除后对象就在数据库中没有对应的记录,也就成transient状态了。

hibernate的状态转换还是比较简单的,当是transient状态时,数据库没有记录对应,而persistent和detached时都有对应的记录,但唯一的区别是detached是在session关闭之后才有的状态。那么transient和detached的区别又是什么呢?就是有没有数据库表记录对应的问题。

2)看完了状态我们来看一下hibernate的缓存
hibernate的缓存分两种,一级缓存和二级缓存。
一级缓存:所谓的一级缓存也就是内部缓存。
二级缓存:它包括应用级缓存,在hibernate就是所谓的SessionFactory缓存,另外一个是分布式缓存,这个是最安全的缓存方式。
直接来看程序:

public static void main(String[] args) { 
  
  Configuration cfg = new Configuration().configure(); 
  SessionFactory sessionFactory = cfg.buildSessionFactory(); 
  Session session = sessionFactory.openSession(); 
      
  User user = (User)session.load(User.class,new Long(29)); 
  System.out.println(user.getName()); 
      
  User user2 = (User)session.load(User.class,new Long(29)); 
  System.out.println(user2.getName()); 
      
  session.close(); 
}
登录后复制

看结果:

Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? 
shun123123 
shun123123
登录后复制

例子中我们用了两次load,但结果中只有一句SQL语句,这表明它只查询了一次。
为什么呢?这也就是hibernate的缓存起作用了。第一次查询完毕后,hibernate后把查出来的实体放在缓存中,下一次查的时候首先会查缓存,看有没有对应ID的实体存在,如果有则直接取出,否则则进行数据库的查询。

下面我们把代码修改成:

User user = (User)session.load(User.class,new Long(29)); 
System.out.println(user.getName()); 
      
session.evict(user);//把user从缓存中删掉 
      
User user2 = (User)session.load(User.class,new Long(29)); 
System.out.println(user2.getName()); 
      
session.close();
登录后复制

看到结果:

Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=?
shun123123
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=?
shun123123
登录后复制

自己我们把user从缓存中删除后,第二次的查询也直接从数据库中取出。

二级缓存小谈
先看实体类:

public class User implements Serializable{
  
  public Long id;
  private String name;
  private int age;
    
}
登录后复制

映射文件就省略啦,大家应该都会写的。
再来看看hibernate配置文件:

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
登录后复制
登录后复制

我们看到provider_class中我们指定了ehcache这个提供类,所以我们也需要ehcache.xml放在classpath中:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
  <diskStore path="java.io.path"/>
  <defaultCache 
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    />
</ehcache>
登录后复制

接下来,我们直接看一下测试方法:

public static void main(String[] args) {
  
  Configuration cfg = new Configuration().configure();
  SessionFactory sessionFactory = cfg.buildSessionFactory();
  Session session = sessionFactory.openSession();
  
  Query query = session.createQuery("from User user where name = &#39;shun123&#39;");
  Iterator iter = query.iterate();
  while(iter.hasNext()) {
    System.out.println(((User)iter.next()).getName());
  }
    
  session.close();
    
  Session session2 = sessionFactory.openSession();
  Query query2 = session2.createQuery("from User user where name=&#39;shun123&#39;");
  Iterator iter2 = query2.iterate();
  while(iter2.hasNext()) {
    System.out.println(((User)iter2.next()).getName());
  }
    
  session2.close();
  
}
登录后复制


运行后可以看到:

Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=?
shun123
Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
shun123
登录后复制

我们可以看到它只执行了一句搜索,而在第二次查询时并没有取出ID进行搜索,这主要归功于二级缓存。

下面我们先分析一下测试方法中的代码。测试方法中我们分别打开了两个Session并且分别创建两个Query进行相同的查询。但两次Session可以共用缓存,这也就是二级缓存,SessionFactory级的缓存。只要我们的Session由同一个SessionFactory创建,那么我们就可以共用二级缓存减少与数据库的交互。
我们再来看一下配置文件中的意思:

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
登录后复制
登录后复制

如果我们需要使用二级缓存,首先需要配置:

<property name="hibernate.cache.use_second_level_cache">true</property>
登录后复制

进行开户二级缓存,然后通过:

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
登录后复制

指定二级缓存的提供类,一般情况下我们都用ehcache,其他我的暂时没用到,也不太清楚,所以暂时不讲了。
像我们刚才的例子,我们只需要配置上面两个,完全可以正常运行,利用二级缓存。
那么第三句是干什么用的呢?

<property name="hibernate.cache.use_query_cache">true</property>
登录后复制

这个配置指明了我们在查询时需要利用缓存,如果我们需要用到这个要事先调用query.setCacheable(true)这个方法来进行启用。

我们一起来看代码(我们先不启用缓存):

public static void main(String[] args) {
  
  Configuration cfg = new Configuration().configure();
  SessionFactory sessionFactory = cfg.buildSessionFactory();
  Session session = sessionFactory.openSession();
  
  Query query = session.createQuery("from User user where name = &#39;shun123&#39;");
  List list = query.list();
  for (int i = 0; i < list.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session.close();
    
  Session session2 = sessionFactory.openSession();
  Query query2 = session2.createQuery("from User user where name=&#39;shun123&#39;");
  List list2 = query2.list();
  for (int i = 0; i < list2.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session2.close();
  
}
登录后复制

这里输出的结果是:

Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
shun123
Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
shun123
登录后复制

我们看到,它并没有利用缓存,因为我们这里用了list,而list对缓存是只写不读的。所以这里会进行两次查询。
那么我们来修改一下:

public static void main(String[] args) {
  
  Configuration cfg = new Configuration().configure();
  SessionFactory sessionFactory = cfg.buildSessionFactory();
  Session session = sessionFactory.openSession();
  
  Query query = session.createQuery("from User user where name = &#39;shun123&#39;");
  <span style="background-color: #ffffff;"><span style="color: #ff0000;">query.setCacheable(true);</span></span>
  List list = query.list();
  for (int i = 0; i < list.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session.close();
    
  Session session2 = sessionFactory.openSession();
  Query query2 = session2.createQuery("from User user where name=&#39;shun123&#39;");
  <span style="color: #ff0000;">query2.setCacheable(true);</span>
  List list2 = query2.list();
  for (int i = 0; i < list2.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session2.close();
  
}
登录后复制

看到红色的两句代码,这是我们进行添加的两个开启查询缓存的代码,现在我们看到结果:

Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
shun123
shun123
登录后复制

只剩一次查询了,为什么呢?就在那两句红色代码处,我们开启了缓存,记住,需要使用两次。把两个query都设成可缓存的才能使用查询缓存。
Criteria也是类似的做法,为免有些童鞋忘记了Criteria怎么写了,我还是放一下代码:

public static void main(String[] args) {
  
  Configuration cfg = new Configuration().configure();
  SessionFactory sessionFactory = cfg.buildSessionFactory();
  Session session = sessionFactory.openSession();
  
  Criteria criteria1 = session.createCriteria(User.class);
  criteria1.setCacheable(true);
  criteria1.add(Restrictions.eq("name","shun123"));
  List list = criteria1.list();
  for (int i = 0; i < list.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session.close();
    
  Session session2 = sessionFactory.openSession();
  Criteria criteria2 = session2.createCriteria(User.class);
  criteria2.setCacheable(true);
  criteria2.add(Restrictions.eq("name","shun123"));
  List list2 = criteria2.list();
  for (int i = 0; i < list2.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session2.close();
  
}
登录后复制

我们看结果:

Hibernate: select this_.USER_ID as USER1_0_0_, this_.USER_NAME as USER2_0_0_, this_.age as age0_0_ from USER this_ where this_.USER_NAME=?
shun123
shun123
登录后复制

更多详解Java的Hibernate框架中的缓存与二级缓存相关文章请关注PHP中文网!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

公司安全软件导致应用无法运行?如何排查和解决? 公司安全软件导致应用无法运行?如何排查和解决? Apr 19, 2025 pm 04:51 PM

公司安全软件导致部分应用无法正常运行的排查与解决方法许多公司为了保障内部网络安全,会部署安全软件。...

如何使用MapStruct简化系统对接中的字段映射问题? 如何使用MapStruct简化系统对接中的字段映射问题? Apr 19, 2025 pm 06:21 PM

系统对接中的字段映射处理在进行系统对接时,常常会遇到一个棘手的问题:如何将A系统的接口字段有效地映�...

如何优雅地获取实体类变量名构建数据库查询条件? 如何优雅地获取实体类变量名构建数据库查询条件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本启动Spring...

如何将姓名转换为数字以实现排序并保持群组中的一致性? 如何将姓名转换为数字以实现排序并保持群组中的一致性? Apr 19, 2025 pm 11:30 PM

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名构建查询条件? 使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名构建查询条件? Apr 19, 2025 pm 09:51 PM

在使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名以构建查询条件,是一个常见的难题。本文将针...

Java对象如何安全地转换为数组? Java对象如何安全地转换为数组? Apr 19, 2025 pm 11:33 PM

Java对象与数组的转换:深入探讨强制类型转换的风险与正确方法很多Java初学者会遇到将一个对象转换成数组的�...

电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? 电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品? Apr 19, 2025 pm 11:27 PM

电商平台SKU和SPU表设计详解本文将探讨电商平台中SKU和SPU的数据库设计问题,特别是如何处理用户自定义销售属...

See all articles