zoukankan      html  css  js  c++  java
  • sgg_5_session缓存

    一、session缓存

      在session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存,只要Session实例没有结束生命周期,且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期。
      Session缓存可减少Hibernate应用程序访问数据库的频率,这也正是提高响应效率的最根本原因。实际上session就是hibernate的一级缓存,sessionFactory则为hibernate的二级缓存。

    二、session缓存的存在证明  

    @Test
        public void sessionCacheTest(){
            Student student1=(Student) session.get(Student.class, 1);
            System.out.println(student1);
            Student student2=(Student) session.get(Student.class, 1);
            System.out.println(student2);
            System.out.println(student1==student2);
        }

      结果截图:

      

      这段程序解释:当第一次调用session.get()时,会先去缓存中找,如果没有则去数据库中找,并且将找出来的数据放入session缓存中,当第二次session.get()和第一个同一个对象时,又先去缓存找,如果缓存中存在,则直接将缓存中的引用给它,这里面明显缓存中存在,所以这时不会再去数据库中取数据了,因为缓存中的数据和数据库中的数据是一个对象。

       三、操作session缓存的方法

        3.1flush()

        flush()方法使得数据库中的数据和session缓存中的数据保持一致。如果数据库中的数据和session缓存中的数据不一致(仅仅是在不一致的情况下才发送更新sql),则会发送sql语句去更新数据库与缓存中的数据保持一致。

        flush()有可能会发送sql语句,但是不会去主动提交事务,提交事务是由docommit()进行提交的。  

        注意:在未提交事务或显示的调用session.flush()方法之前,也有可能会进行flush()操作。
        比如当执行HQL或QBC查询,会先进行flush()操作,以得到数据库表的最新的记录。
        若记录的ID是由底层数据库使用自增的方式生成的,则在调用save()方法后,就会立即发送insert语句,因为save方法后,必须保证对象的ID是存在的!若记录的ID是由hibernate为我们生成的则会走到commit()方法时才会发送sql语句。  

      Session按照缓存中对象的属性变化来同步更新数据库
      默认情况下Session在一下时间点刷新缓存:
        1.显示调用Session的flush()方法
        2.当应用程序调用Transaction的commit()方法时,该方法先flush(),然后在向数据库提交事务
        3.当应用程序执行一些查询(HQL Criteria)操作时,如果缓存中持久化对象的属性已经发生变化,会先flush()缓存以保证查询结果能够反映持久化对象的最新状态。
      flush缓存的例外情况:
        如果对象使用native生成器生成OID,那么当调用Session的sava()方法保存对象时,会立即执行向数据库插入该实体的insert语句。
      commit()和flush()方法的区别:
        flush执行一系列sql语句,但不提交事务;commit()方法先调用flush()方法,然后提交事务,意味着对数据库操作永久保存下来。  

      设定刷新缓存的时间点
        若希望改变flush的默认时间点,可以通过Session的setFlushMode()方法显式设定flush的时间点。

      

    @Test
        public void sessionFlushTest(){
            Student student=(Student) session.get(Student.class, 1);
            student.setStuAge(21);
        }

     运行结果:

      

        

      

     这段代码是将一个对象加载到session缓存中,然后改了年龄这个属性,改完之后事务提交之前会默认调用flush()方法(根据跟源码所知),所以此时数据库中的数据和session缓存中的数据明显不一致了,所以会发送sql去更新数据库,以保持数据库中的数据记录与对应的缓存中的记录保持一致。  

      注意:在未提交事务或显示的调用session.flush()方法之前,也有可能会进行flush()操作。
      比如当执行HQL或QBC查询,会先进行flush()操作,以得到数据库表的最新的记录。
      若记录的ID是由底层数据库使用自增的方式生成的,则在调用save()方法后,就会立即发送insert语句,因为save方法后,必须保证对象的ID是存在的!但如果这个ID是由hibernate为我们生成的,那么在commit()时才会发送sql语句。

      3.2refresh()

      session的refresh()方法会强制缓存中的数据和数据库中的数据保持一致,调用此方法,会向数据库发送一条查询语句以获取数据库中数据的最新状态,调用这个方法之前需要注意mysql的隔离级别。接下来我们说下。测试用断点+手动改数据库字段的方式。  

    @Test
        public void sessionRefreshTest(){
            Student student=(Student) session.get(Student.class, 1);
            System.out.println(student);
            session.refresh(student);
            System.out.println(student);
            
        }

      如果采用mysql默认事务隔离级别,那么refresh()前的student和之后的student的状态一致,但是当把mysql的事务隔离级别改为读已提交,那么我们可以通过断点+手动改字段的方式可知refresh()之后读取的是我们改之后的,也就是数据库中的最新状态。当然也可以通过hibernate.cfg.xml中设置事务的隔离级别,其配置为:  

     <!-- 设置数据库的隔离级别 -->
     <property name="connection.isolation">2</property>

      数据库的隔离级别

      对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题
      1.脏读:对于两个事务T1,T2,若T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时无效的。
      2.不可重复读:对于两个事务T1,T2,若T1读取了一个字段,然后T2更新了该字段,之后T1再次读取同一个字段,值就不同了。
      3.幻读:对于两个事务T1,T2若T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后如果T1再次读取同一个表,就会多出几行。
      数据库事务的隔离性:数据库系统必须具有隔离并发运行的各个事务的能力,使它们不会相互影响,避免各种并发问题。
      一个事务与其他事务隔离的程度称为隔离级别,数据库规定了多种事务隔离级别,不同隔离级别对应不同干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
      数据库提供的4中事务隔离级别:
        1.READ UNCOMMITED(读未提交数据):允许事务读取未被其他事务提交的变更,脏读,不可重复读和幻读的问题都会出现。
        2.READ COMMITED(读已提交数据):只允许事务读取已经被其它事务提交的变更,可以避免脏读,但不可重复读和幻读任然可能出现。
        3.REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其它事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题任然存在。
        4.SERIALLZABLE(串行化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作,所有并发问题都可以避免,但性能十分低下。
      Oracle支持的2中事务隔离级别:READ COMMITED,SERIALLZABLE。Oracle默认的事务隔离级别为:READ COMMITED
      Mysql支持4种事务隔离级别,默认事务隔离级别为REPEATABLE READ
      在Mysql中设置隔离级别
      每启动一个mysql程序,就会获得一个单独的数据库连接,每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别,Mysql默认的隔离级别为:Repeatable Read
      查看你当前的隔离级别:select @@tx_isolation;
      设置当前mysql连接的隔离级别:
        settransaction isolation level read committed;
      设置数据库系统的全局的隔离级别:
        set global transaction isolation level read committed;
      在hibernate中设置隔离级别
      jdbc数据库连接使用数据库系统默认的隔离级别,在hibernate的配置文件中以显示的设置隔离级别,没一个隔离级别都对应一个整数:
        1.READ UNCOMMITED
        2.READ COMMITED;
        3.REPAEATABLE READ;
        4.SERIALLZEBLE
      hibernate通过Hibernate映射文件制定hibernate.connection.isolation属性来设置事务的隔离级别。

       3.3clear()清理缓存

      session的clear()方法可以清空session缓存中的所有数据。

    @Test
        public void sessionClearTest(){
            Student student1=(Student) session.get(Student.class, 1);
            session.clear();
            Student student2=(Student) session.get(Student.class, 1);
            
        }

      之前我们知道session的get方法查同一标识符对象时只会发送一条select sql语句,之后无论查多少次,在session没有关闭的情况下,都会从缓存中取数据,如果我们在中间加入清理缓存的方法clear(),那么我们可以明显的看出来缓存中没有数据时又向数据库中发送了select 的查询sql语句。

    如有任何疑问可联系邮箱: 给我发邮件、或直接联系QQ:1584875179 || 点返回首页

  • 相关阅读:
    git变慢的原因
    MongoDB存储过程创建和使用一例
    关于小游戏的槛和限制
    【转载】如何查看本机电脑的公网IP
    【转载】C#如何获取DataTable中某列的数据类型
    【转载】C#的DataTable使用NewRow方法创建新表格行
    【转载】如何删除Windows远程桌面保存的账号密码数据
    【转载】 C#中ArrayList使用GetRange方法获取某一段集合数据
    【转载】 C#中常见的泛型集合类有哪些
    【转载】C#中使用Insert方法往ArrayList集合指定索引位置插入新数据
  • 原文地址:https://www.cnblogs.com/aeon/p/10836229.html
Copyright © 2011-2022 走看看