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缓存清空了。




   

原文地址:https://www.cnblogs.com/elgin-seth/p/5293780.html