zoukankan      html  css  js  c++  java
  • Hibernate之Session缓存以及操作Session缓存的相关方法

    1、Session概述

         A、Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法.

         B、 Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应. Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush).

         C、站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.

    2、操作 Session 缓存

       

          上述的图表描述了Session缓存相关方法的作用和意义,即:

    flush():按照缓存中对象的属性的变化来同步更新数据库

    reflesh():根据数据库中的记录信息来更新缓存对象中的信息

    clear() : 清除缓存中的持久化对象信息

    3、Session对象相关方法:

         下面通过代码我们来分别对Session的各个方法进行测试说明,其它相关配置文件在这里不做特殊说明,有问题可直接看HelloWorld中的相关配置。

         首先,新建一个单元测试类如下所示:   

    package com.elgin.hibernate.test;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.service.ServiceRegistry;
    import org.hibernate.service.ServiceRegistryBuilder;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import com.elgin.hibernate.entity.News;
    
    public class HibernateTest {
         
    	//如此声明只为方便测试,正式环境不能这么用
    	private SessionFactory sessionFactory;
    	private Session session;
    	private Transaction transcation;
    	
    	@Before
    	public void init(){
    		Configuration cfg=new Configuration().configure();
    		ServiceRegistry serviceRegistry=new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
    		sessionFactory=cfg.buildSessionFactory(serviceRegistry);
    		session=sessionFactory.openSession();
    		transcation=session.beginTransaction();
    	}
    	
    	@After
    	public void destory(){
    		transcation.commit();
    		session.close();
    		sessionFactory.close();
    	}
    }
    
    一、首先来测试Session缓存,在上述代码基础上增加如下测试方法:

    @Test
    	public void testSessionCache(){
    		News news=(News) session.get(News.class, 1);
    		System.out.println(news.toString());
    		News news2=(News) session.get(News.class, 1);
    		System.out.println(news2.toString());
    		System.out.println(news==news2);
    	}
    运行之后,会在控制台打印出如下信息:

    Hibernate: 
        select
            news0_.ID as ID1_0_0_,
            news0_.TITLE as TITLE2_0_0_,
            news0_.AUTHOR as AUTHOR3_0_0_,
            news0_.DATE as DATE4_0_0_ 
        from
            NEWS news0_ 
        where
            news0_.ID=?
    News [id=1, title=少年闰土, author=Oracel, date=2015-07-28 00:00:00.0]
    News [id=1, title=少年闰土, author=Oracel, date=2015-07-28 00:00:00.0]
    true

    对于同一个id对应的对象,如果在Session缓存中存在,那么就不会再去从数据库查询,而是直接从Session缓存中读取

    二、Session接口的flush() 方法测试:

    /**
    	 * flush()方法使数据表中的记录和session缓存中对象状态保持一致,为了保持一致,则可能向数据库发送对应的sql语句
    	 * 1.调用commit()方法中,是先调用flush()方法,再提交事务
    	 * 2.fulsh()方法可能会发送sql语句,但不会提交事务
    	 * 注意:在未提交事务或者显式的调用session.flush()方法之前,也有可能执行flush()方法
    	 *  1、执行HQL、QBC查询的,会先进行flush()操作,以得到数据库中最新的数据
    	 *  2、若记录的id是使用数据库自增的方式生成,则在调用save()方法之后,会立即发送insert语句来保证对象的id是存在的
    	 */
    	@Test
    	public void testSessionFlush(){
    		News news=(News) session.get(News.class, 1);
    		news.setAuthor("李白");
    	}
    执行之后,你会发现数据库中对应的author变成了李白,并没有使用update方法来更新数据库,为什么数据库内的内容会发生变化?

    其实,这就是Session的flush()方法在起作用了,也许你还会问,这里也并没有调用flush()方法呢,因为在调用commit()方法来提交事务的时候,在commit()方法的实现中调用了flush()方法,可以在commit()方法处打断点,然后debug,你会发现,在执行commit()方法之前,数据库的内容并没有发生变化,除了get方法发出的sql查询语句,并没有发出sql语句。控制台内容如下:

    Hibernate: 
        select
            news0_.ID as ID1_0_0_,
            news0_.TITLE as TITLE2_0_0_,
            news0_.AUTHOR as AUTHOR3_0_0_,
            news0_.DATE as DATE4_0_0_ 
        from
            NEWS news0_ 
        where
            news0_.ID=?
    Hibernate: 
        update
            NEWS 
        set
            TITLE=?,
            AUTHOR=?,
            DATE=? 
        where
            ID=?

    三、Session的refresh()方法测试:

    /**
    	 * refresh()方法会强制发送select语句,以使缓存中对象的信息跟数据表中信息一致
    	 * 
    	 */
    	@Test
    	public void testRefresh(){
    		News news=(News) session.get(News.class, 1);
    		System.out.println(news);
    		//debug执行到这一步之前,改变表中数据测试
    		session.refresh(news);
    		System.out.println(news);
    	}
           测试refresh方法,使用debug运行Junit,断点打在refresh方法那一行,在执行refresh() 方法之前,打印语句之后,任意改变数据库中词条记录中的内容,然后执行剩余方法。由于知道了refresh方法的作用,预期news中的内容会变成数据库中我们修改后最新的信息,如果你使用的数据库是MySQL,那么你会发现,2次打印出的内容是一致的,第二次打印出的内容并不包含我们修改后的信息,这是为什么呢?

          其实这个数据库事务的隔离级别有关系,MySQL默认的隔离级别是:REPEATABLE READ,也就是可重复度。具体作用是确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其它事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读依旧存在。

        在Hibernate中,我们可以在Hibernate的配置文件中设置事务的隔离级别,

      每一个隔离级别都对应一个整数:

         1. READUNCOMMITED

         2. READ COMMITED

        4. REPEATABLEREAD

        8. SERIALIZEABLE

        Hibernate 通过为 Hibernate 映射文件指定hibernate.connection.isolation 属性来设置事务的隔离级别

    在Hibernate的配置问价中增加如下配置语句:

    <!--设置hibernate事务的隔离级别  -->
           <property name="connection.isolation">2</property>
    之后重新按照刚才的方法运行,会发现2次打印出来的news对象不同,这正是refresh() 方法的作用:refresh()方法会强制发送select语句,以使缓存中对象的信息跟数据表中信息一致。

    四、Session的clear() 方法测试

    @Test
    	public void testClear(){
    		News news=(News) session.get(News.class, 1);
    		session.clear();
    		News news2=(News) session.get(News.class, 1);
    	}
    执行上述测试代码发现,Hibernate分别发送了2条sql语句来进行查询,之前我们说,对于同一个id对应的对象,如果在Session缓存中存在,那么就不会再去从数据库查询,而是直接从Session缓存中读取,而在此却是查询了2次数据库,这说明在执行clear() 方法时,将Session缓存清空了。




       

  • 相关阅读:
    B/S 和 C/S
    SQL 注入
    软件测试
    Spring的注解方式
    测试开发题目
    策略模式
    设计模式
    单例模式
    读写文件
    对List里的对象元素进行排序
  • 原文地址:https://www.cnblogs.com/elgin-seth/p/5293780.html
Copyright © 2011-2022 走看看