Hibernate - SessionFactory和Session详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/J080624/article/details/82937616

【1】SessionFactory 接口

SessionFactory 接口是针对单个数据库映射关系经过编译后的内存镜像,是线程安全的。

SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息。

SessionFactory是生成Session的工厂,构造 SessionFactory 很消耗资源,一般情况下一个应用中只初始化一个 SessionFactory 对象。

Hibernate4 新增了一个 ServiceRegistry 接口,所有基于 Hibernate 的配置或者服务都必须统一向这个 ServiceRegistry 注册后才能生效

Hibernate4.0之前创建 SessionFactory 的步骤:

// 创建 Configuration 对象: 对应 hibernate 的基本配置信息和 对象关系映射信息
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
		
//4.0 之前这样创建
SessionFactory sessionFactory = configuration.buildSessionFactory();

Configuration 类负责管理 Hibernate 的配置信息。包括如下内容:

  • Hibernate 运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)。
  • 持久化类与数据表的映射关系(*.hbm.xml 文件)

Hibernate4.X中创建 SessionFactory 的步骤:

// 创建一个 ServiceRegistry 对象: hibernate 4.x 新添加的对象
//hibernate 的任何配置和服务都需要在该对象中注册后才能有效.

ServiceRegistry serviceRegistry = 
new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
//或如下方式
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();

//创建一个 SessionFactory 对象
SessionFactory  sessionFactory = configuration.buildSessionFactory(serviceRegistry);

Hibernate5.X中创建 SessionFactory 的步骤:

StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().configure().build();
SessionFactory 	sessionFactory = new MetadataSources(ssr).buildMetadata().buildSessionFactory();

【2】Session 接口

Session 是应用程序与数据库之间交互操作的一个单线程对象,生命周期很短,是 Hibernate 运作的中心,所有持久化对象必须在 session 的管理下才可以进行持久化操作(持久化类与 Session 关联起来后就具有了持久化的能力)。

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

Session 类的方法:

  • 取得持久化对象的方法: get(), load()
  • 持久化对象都得保存,更新和删除:save(),update(),saveOrUpdate(),delete()
  • 开启事务: beginTransaction().
  • 管理 Session 的方法:isOpen(),flush(), clear(), evict(), close()等

Session 对象有一个一级缓存,显式执行 flush 之前,所有的持久层操作的数据都缓存在 session 对象处,位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应。

Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush)。

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


① session缓存 - 一级缓存

在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存。 只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期。

Session 缓存可减少 Hibernate 应用程序访问数据库的频率。

测试代码如下:

	@Before
	public void init(){
		StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().configure().build();
		sessionFactory = new MetadataSources(ssr).buildMetadata().buildSessionFactory();
		
		session = sessionFactory.openSession();
		transaction = session.beginTransaction();
	}
	
	@After
	public void destroy(){
		transaction.commit();
		session.close();
		sessionFactory.close();
	}
	
	@Test
	public void test1(){
		News news = (News) session.get(News.class, 1);
		System.out.println(news); 
		
		News news2 = (News) session.get(News.class, 1);
		System.out.println(news2);
	}	

测试结果如下图:

在这里插入图片描述

本质上用的是同一个session,由于session一级缓存关系,在第一次发送SQL语句查询后第二次直接使用缓存中的数据,不会再发送SQL。除非session缓存被清空!

如下图所示,Hibernate提供了三种方式操作session缓存:

在这里插入图片描述


② 缓存操作之flush

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

默认情况下 Session 在以下时间点刷新缓存:

  • 显式调用 Session 的 flush() 方法;
  • 当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后再向数据库提交事务;
  • 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态。

  • 设定刷新缓存的时间点

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

清理缓存的模式 各种查询方法 Transaction.commit() Session.flush()
FlushMode.AUTO(默认) 清理 清理 清理
FlushMode.COMMIT 不清理 清理 清理
FlushMode.NEVER 不清理 不清理 清理

  • flush 缓存的例外情况

如果对象使用 native 生成器生成 OID, 那么当调用 Session 的 save() 方法保存对象时, 会立即执行向数据库插入该实体的 insert 语句。因为 save 方法后, 必须保证对象的 ID 是存在的 !


  • commit() 和 flush() 方法的区别

flush 操作可能会执行一系列 sql 语句,但不提交事务;commit 方法先调用flush() 方法,然后提交事务。提交事务意味着对数据库操作永久保存下来。

测试代码如下:

	@Test
	public void testSessionFlush(){
		News news = (News) session.get(News.class, 1);
		news.setAuthor("Hibernate");
		
		session.flush();
		System.out.println("flush");//这里打断点,debug
		
		News news2 = (News) session.createCriteria(News.class).list().get(0);
		System.out.println(news2);//这里打断点,debug,并注释掉session.flush()
	}

在这里插入图片描述


在这里插入图片描述


③ 缓存操作之refresh

refresh()会强制发送 SELECT 语句, 以使 Session 缓存中对象的状态和数据表中对应的记录保持一致!

测试代码如下:

	@Test
	public void testRefresh(){
		News news = (News) session.get(News.class, 1);
		System.out.println(news);
		
		session.refresh(news); //这里打断点,手动改数据库
		System.out.println(news); 
	}

测试结果如下:

Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
News [id=1, title=hibernate, author=Oracle, describle=orm, date=2018-10-04 11:39:38.0, content=null, picture=null]

//这里再次发送了查询语句
Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
News [id=1, title=hibernate, author=Oracle, describle=orm, date=2018-10-04 11:39:38.0, content=null, picture=null]

//但是结果并没有改变--在debug断点处手动改变了Oracle为JPA。

可以看到refresh已经工作,但是news并非最新状态,为什么?

这是由于MySQL中事务隔离级别导致的。MySQL默认的事务隔离级别为可重复读,即在一个事务内,虽然refresh又进行了一次查询,但是读取的数据还是该事务中第一次读取的数据,非最新数据!


  • 在 Hibernate 中设置隔离级别

JDBC 数据库连接使用数据库系统默认的隔离级别。在 Hibernate 的配置文件中可以显式的设置隔离级别. 每一个隔离级别都对应一个整数:

1. READ UNCOMMITED
2. READ COMMITED
4. REPEATABLE READ
8. SERIALIZEABLE

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

<!-- 设置 Hibernate 的事务隔离级别 2表示读已提交 -->
<property name="connection.isolation">2</property>
	

再次测试refresh方法结果如下:

Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
News [id=1, title=hibernate, author=JPA, describle=orm, date=2018-10-04 11:39:38.0, content=null, picture=null]
Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
News [id=1, title=hibernate, author=Oracle, describle=orm, date=2018-10-04 11:39:38.0, content=null, picture=null]

可以看到两个news中author已经不同!

参考博文:

MySQL中事务详解
一文读懂Spring事务和MySQL事务


④ 缓存操作之clear

clear()将会清理掉session的缓存。

测试代码如下:

	@Test
	public void testClear(){
		News news1 = (News) session.get(News.class, 1);
		
		session.clear();
		
		News news2 = (News) session.get(News.class, 1);
	}

测试结果如下:

Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DESCRIBLE as DESCRIBL4_0_0_,
        news0_.DATE as DATE5_0_0_,
        news0_.CONTENT as CONTENT6_0_0_,
        news0_.PICTURE as PICTURE7_0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?

可以看到,发送了两条查询语句!

猜你喜欢

转载自blog.csdn.net/J080624/article/details/82937616