hibernate对象状态与一级缓存二级缓存

1 hibernate 中 持久类对象状态介绍

1.1hibernate 规定三种状态:瞬时态、持久态、脱管态


    瞬时态:transient,     session没有缓存对象,数据库也没有对应记录。
        OID特点:没有值
    持久态:persistent,   session缓存对象,数据库最终会有记录。(事务没有提交)
        OID特点:有值
    脱管态:detached,    session没有缓存对象,数据库有记录。
        OID特点:有值

1.2 状态之间的转换

 

1.3  瞬时态/临时态

  1. 获得:一般都是直接创建(new
  2. 瞬时态---->持久态

       一般操作:save方法、saveOrUpdate

     3  瞬时态------>脱管态

       一般操作:通过setId方法设置数据

例如:

       User user = new User();        //瞬时态

       user.setUid(1);                       //脱管态

1.4 持久态

1.获得:
               查询操作:get、loat、createQuery、createCriteria 等 获得都是持久态
                执行save之后持久态
               执行update之后持久态
2.   持久态------->瞬时态
                 官方规定执行delete()  --民间:删除态
3. 持久态--------->脱管态
               session没有记录
               session.close () 关闭
                session.clear() 清除所有
                session.evict(obj) 清除指定的PO对象

1.5 脱管态/游离态

1.获得:
            创建、并设置OID的
             通过api获得
2. 脱管态------->瞬时态
          手动去除OID,设置成默认值
3.脱管态-------->持久态
一般操作:update()、saveOrUpdate

1.6  测试

   @Test

   public void demo01(){

      User user = new User();  //瞬时态

      user.setUsername("jack");

      user.setPassword("1234"); //瞬时态(与oid没有关系)

     

      Session session = factory.openSession();

      session.beginTransaction();

     

      session.save(user);   //持久态

//---- 持久态就应该有持久态的行为(特性)

//user.setUsername("rose");  //持久态对象 被修改后,hibernate将自动生成update语句

//session.flush();

      session.getTransaction().commit();

      session.close();

      System.out.println(user);  //脱管态

   }

2 一级缓存

    一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

2.1 证明一级缓存

cfg.xml中配置数据库语句显示

测试一

@Test

   public void demo02(){

      //证明一级缓存

      Session session = factory.openSession();

      session.beginTransaction();

      //1 查询 id = 1

      User user = (User) session.get(User.class, 1);

      System.out.println(user);

      //2 再查询 -- 不执行select语句,将从一级缓存获得

      User user2 = (User) session.get(User.class, 1);

      System.out.println(user2);

      session.getTransaction().commit();

      session.close();

   }

测试二

@Test

   public void demo03(){

      //清除缓存

      Session session = factory.openSession();

      session.beginTransaction();

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

      //清除

      //session.clear();

      session.evict(user);

      // 一级缓存没有缓存对象,从数据库直接查询

      User user2 = (User) session.get(User.class, 1);  //--select

      System.out.println(user2);

      session.getTransaction().commit();

      session.close();

   }

2.2 一级缓存快照

快照:

与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。

如果一级缓存修改了,

执行commit提交时,将自动刷新一级缓存,

执行update语句,将一级缓存的数据更新到数据库

 

refresh 刷新

refresh 保证 一级缓存的数据 与 数据库的数据 保持一致

将执行select语句查询数据库,将一级缓存中的数据覆盖掉只要执行refresh都将执行select语句。

@Test

   public void demo04(){

      //刷新

      Session session = factory.openSession();

      session.beginTransaction();

     

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

     

      session.refresh(user);

     

      session.getTransaction().commit();

      session.close();

   }

快照演示(一级缓存刷新)

@Test

   public void demo05(){

      //快照

      Session session = factory.openSession();

      session.beginTransaction();

     

      User user = (User) session.get(User.class, 1);  //--select

      System.out.println(user);

     

      //修改持久态对象内容(一级缓存内容)--默认在commit时,将触发update语句

      user.setUsername("rose2");

     

      session.getTransaction().commit();

      session.close();

   }

  1. 问题:一级缓存什么时候刷新?(了解)

       默认情况提交(commit())刷新。

@Test

   public void demo06(){

      //设置刷新时机

      Session session = factory.openSession();

      session.beginTransaction();

     

      //1 设置

      session.setFlushMode(FlushMode.MANUAL);

     

      User user = (User) session.get(User.class, 1);

      user.setUsername("rose4");

     

      //1 查询所有 -- AUTO , 查询之前先更新,保存一级缓存和数据库一样的

      //List<User> allUser = session.createQuery("from User").list();

     

      //2手动刷新 --MANUAL 将执行update,注意:一级缓存必须修改后的

      session.flush();

     

      // 如果MANUAL 在执行commit 不进行update

      session.getTransaction().commit();

      session.close();

   }

三  二级缓存

缓存(Cache): 计算机领域非常通用的概念。它介于应用程序永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存

       缓存:程序<--(内存)-->硬盘

3.1 什么是二级缓存

  1. hibernate 提供缓存机制:一级缓存、二级缓存

       一级缓存:session级别缓存,在一次请求中共享数据。

       二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。

  1. SessionFactory的缓存两部分:   内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。

     2  外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。

3.2 二级缓存内部结构

二级就是由4部分构成
    类级别缓存
    集合级别缓存
    时间戳缓存
    查询缓存(二级缓存的第2大部分,三级缓存)

3.3 并发访问策略

  1. 访问策略:读写型(read-write)、只读型(read-only

3.4 应用场景

  1. 适合放入二级缓存中的数据:

       很少被修改

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

      2.不适合放入二级缓存中的数据:

       经常被修改

       财务数据, 绝对不允许出现并发问题

       与其他应用数据共享的数据

3.5 二级缓存提供商

  1. EHCache: 可作为进程(单机)范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。--支持集群。
  2. OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
  3. SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
  4. JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存

 

X表示支持

3.6 ehcache配置二级缓存

1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar

2.开启二级缓存(我要使用二级缓存)

  1. 在hibernate.cfg.xml 配置二级缓存

<!-- 9.1 开启二级缓存 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

3.确定二级缓存提供商(我要使用哪个二级缓存)

  1. hibernate.cfg.xml 配置

<!-- 9.2 提供商 -->

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

4.确定需要缓存内容

  在hibernate.cfg.xml 确定 类缓存 和集合缓存配置项

<!-- 9.3 确定缓存内容 -->

      <!-- 类缓存 -->

      <class-cache usage="read-write" class="com.test.Customer"/>

      <class-cache usage="read-write" class="com.test.Order"/>

      <!-- 集合缓存 -->

   <collection-cacheusage="read-write"collection="com.itheima.a_init.Customer.orderSet"/>

       1>配置需要缓存的类

       2>配置需要缓存的集合

5.配置ehcache自定义配置文件

步骤1:从jar包复制xml文件

      

步骤2:将xml重命名“ehcache.xml”

      

步骤3:将修改后的xml,拷贝到src下

      

6 测试

6.1 证明

@Test

   public void demo01(){

      //1 证明二级缓存存在

      // * 修改toString()

      // * 如果二级缓存开启,查询3 没有select语句,表示从二级缓存获得的。

      // * 将二级缓存关闭,查询3将触发select语句。

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查询id=1 -- 执行select (查询后,将数据存放在一级缓存,之后由一级缓存同步到二级缓存)

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 查询id=1 --从一级缓存获取

      Customer c2 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c2);

     

      s1.getTransaction().commit();

      s1.close();

     

      System.out.println("----------");

     

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //3 查询id=1 -- 从二级缓存获取

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

     

      s2.getTransaction().commit();

      s2.close();

   }

6.2 类缓存

  1. 类缓存:只存放数据
  2. 一级缓存:存放对象本身

@Test

   public void demo02(){

      //2 类缓存:只存放数据,散装数据。

      // * 使用默认的toString();

      Session s1 = factory.openSession();

      s1.beginTransaction();

      //1 查询id=1 -- 执行select

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 查询id=1 -- 从一级缓存获取,一级缓存存放对象本身

      Customer c2 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c2);

     

      s1.getTransaction().commit();

      s1.close();

     

      System.out.println("----------");

     

      Session s2 = factory.openSession();

      s2.beginTransaction();

      //3 查询id=1 -- 对象不一样,数据一样

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

     

      s2.getTransaction().commit();

      s2.close();

   }

 6.3 集合缓存

@Test

   public void demo03(){

       //3 集合缓存:只存放关联对象OID的值,如果需要数据,从类缓存中获取。

      // * 3.1 默认:第一条select 查询客户,第二天 select 查询客户所有订单

      // * 3.2 操作:在hibernate.cfg.xml Order 类缓存删除

      // *** <!--  <class-cache usage="read-write" class="com.itheima.a_init.Order"/>-->

      // *** 多了10select,通过订单的id查询订单

      Session s1 = factory.openSession();

      s1.beginTransaction();

     

      //1 查询id=1

      Customer c1 = (Customer) s1.get(Customer.class, 1);

      System.out.println(c1);

      //2 获得订单

      for (Order o1 : c1.getOrderSet()) {

         System.out.println(o1);

      }

     

      s1.getTransaction().commit();

      s1.close();

     

      System.out.println("----------");

     

      Session s2 = factory.openSession();

      s2.beginTransaction();

     

      //3 查询id=1

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

      //4 获得订单

      for (Order o2 : c3.getOrderSet()) {

         System.out.println(o2);

      }

     

      s2.getTransaction().commit();

      s2.close();

   }

7 时间戳

  1. 时间戳:任何操作都在时间戳中记录操作时间。

 

@Test

   public void demo04(){

      //4 时间戳: 所有的操作都会在时间戳中进行记录,如果数据不一致,将触发select语句进行查询

      // * 修改toString()

      Session s1 = factory.openSession();

      s1.beginTransaction();

     

      //1 查询id=1

      Integer cid = 1;

      Customer c1 = (Customer) s1.get(Customer.class, cid);

      System.out.println(c1);

      //2 绕过一级和二级缓存,修改数据库,修改客户cname=大东哥

      s1.createQuery("update Customer set cname = ? where cid = ?")

         .setString(0, "大东哥")

         .setInteger(1, cid)

         .executeUpdate();

      //3打印

      System.out.println(c1);

     

      s1.getTransaction().commit();

      s1.close();

     

      System.out.println("----------");

     

      Session s2 = factory.openSession();

      s2.beginTransaction();

     

      //4 查询id=1  -- ?

      Customer c3 = (Customer) s2.get(Customer.class, 1);

      System.out.println(c3);

     

      s2.getTransaction().commit();

      s2.close();

   }

 8 查询缓存

  1. 查询缓存又称为三级缓存(民间)
  2. 查询缓存默认不使用。需要手动开启
  3. 查询缓存:将HQL语句与 查询结果进行绑定。通过HQL相同语句可以缓存内容。

       默认情况Query对象只将查询结果存放在一级和二级缓存,不从一级或二级缓存获取。

       查询缓存就是让Query可以从二级缓存获得内容。

步骤一:开启查询缓存

      

   <!-- 9.4 开启查询缓存 -->

      <property name="hibernate.cache.use_query_cache">true</property>

     

步骤二:在查询query对象,设置缓存内容(注意:存放和查询 都需要设置)

      

 

             

 

@Test

   public void demo05(){

      //5 查询缓存

      Session s1 = factory.openSession();

      s1.beginTransaction();

     

      //1 query查询

      Query q1 = s1.createQuery("from Customer");

      q1.setCacheable(true);

      List<Customer> a1 = q1.list();

      for (Customer c1 : a1) {

         System.out.println(c1);

      }

     

      //2 cid =1  -- 一级缓存获得

      Customer customer = (Customer) s1.get(Customer.class, 1);

      System.out.println(customer);

     

      s1.getTransaction().commit();

      s1.close();

     

      System.out.println("----------");

     

      Session s2 = factory.openSession();

      s2.beginTransaction();

     

      //2 cid =1  -- 二级缓存获得

      Customer customer2 = (Customer) s2.get(Customer.class, 1);

      System.out.println(customer2);

     

      //3 query查询

      Query q2 = s2.createQuery("from Customer");

      q2.setCacheable(true);

      List<Customer> a2 = q2.list();

      for (Customer c2 : a2) {

         System.out.println(c2);

      }

     

      s2.getTransaction().commit();

      s2.close();

   }

9 ehcache配置文件

  1. <diskStore path="java.io.tmpdir"/>  设置临时文件存放位置。(缓存一般内存,一定程度时,写入硬盘。)

      

  1. 缓存详细设置

       <defaultCache> 所有的缓存对象默认的配置

       <cache name="类"> 指定对象单独配置

  1. 参数设置

           maxElementsInMemory="10000"  内存最大数

           eternal="false"  是否永久(内存常驻留)

           timeToIdleSeconds="120"

          timeToLiveSeconds="120"

          overflowToDisk="true"  内存满了,是否写入到硬盘

            maxElementsOnDisk="10000000"  硬盘最大数

         diskPersistent="false"  关闭JVM,是否将内存保存硬盘中

         diskExpiryThreadIntervalSeconds="120"  轮询

           memoryStoreEvictionPolicy="LRU"

       Least Recently Used (specified as LRU).

       First In First Out (specified as FIFO)

       Less Frequently Used (specified as LFU)

    • maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
  • eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds timeToLiveSeconds属性; 默认值是false
  • timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
  • timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
    如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
  • overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
  • diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
  • diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
  •  memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

 

猜你喜欢

转载自blog.csdn.net/uotail/article/details/81813955
今日推荐