Session接口及方法(flush, refresh,clear,,)

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

Session 具有一个缓存(一级缓存), 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应. Session 能够在某些时间点, 按照缓

存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush) 站在持久化的角度, Hibernate 把对象分为

4 种状态:持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.

范例: 在HelloWorld的基础上演示Session的操作

  把News.java属性date换成java.util.Date类型, 修改News.hbm.xml: <property name="date" type="java.util.Date">

java.util.Date对应着数据库中的datetime, 即时间戳, 有日期, 也有时间, java.sql.Date对应着数据库中的date, 只有日期, 没有时间.

工程结构:

com.atguigu.hibernate.entities.HibernateTest.java:

  在init()中创建SessionFactory, Session, Transaction对象

  在destroy()中提交事务, 关闭Session, 关闭SessionFactory

package com.atguigu.hibernate.entities;

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;

public class HibernateTest {

// 成员变量
private SessionFactory sessionFactory; // 是线程安全的
// 注意: 在实际开发中, Transaction和Session不能作为成员变量, 会引发并发问题
private Transaction transaction;
private Session session;

@Before
public void init() {
// 创建SessionFactory对象
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
// 创建Session对象
session = sessionFactory.openSession();
// 创建事务对象
transaction = session.beginTransaction();
}

@Test
public void testSessionCache() {
  
}

@After
public void destroy() {
// 提交事务
transaction.commit();
// 关闭Session
session.close();
// 关闭sessionFactory
sessionFactory.close();
}
}

测试:

----------------------------------准备工作完成------------------------------------------

 范例1: Session缓存(一级缓存)

@Test
public void testSessionCache() {
News news = (News) session.get(News.class, 1);
System.out.println(news);

News news2 = (News) session.get(News.class, 1);
System.out.println(news);
}

创建了两个News对象, 但是后台只打印一条SQL语句:

说明:

  在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有清理缓存

则存放在它缓存中的对象也不会结束生命周期. 

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

 范例2: 清理Session缓存

 

@Test
public void testSessionFlush() {
News news = (News) session.get(News.class, 1);
news.setAuthod("Oracle");
System.out.println(news);
}

 分析: 在Session生命周期中, 在事务提交之前执行setAuthod(), hibernate会感知到数据库中的数据和缓存中的数据不一致,

会调用flush方法发送UPDATE语句

flush方法: 使数据表中的记录和缓存中的数据保持一致, 在commit()之前先执行flush方法, 如果数据没有变化, 不发送SQL语句

如果不一致, 则发送SQL语句

后台打印:

  session.flush(); 可以显示调用sesion的flush, 会立即发送SQL, 但数据库中的记录还不会发生改变, 因为还没有提交事务但是在未提交事务

或者显示调用flush方法之前, 也有可能进行flush操作, 比如执行HQL或QBC(Query By Criteria)查询,  因为要得到最新的数据. 比如:

News news2 = (News) session.createCriteria(News.class).uniqueResult();  

以上语句进行QBC查询, 所以要进行flush操作, 会发送UPDATE语句, 但是数据库中的记录直到commit之后才会更新

--------------------------------------------

范例3: refresh()

 

(1) 测试1: 执行到断点处, 去修改数据库中的值

@Test
public void testSessionRefresh() {
News news = (News) session.get(News.class, 1);
System.out.println(news);  
System.out.println(news);  // 这里打断点
}

连续打印两条记录, 后台只发送一条SELECT语句, 打印的是数据库数修改前的数据

(2) 测试2: 执行到断点处, 去修改数据库中的值

@Test
public void testSessionRefresh() {
News news = (News) session.get(News.class, 1);
System.out.println(news);
// 在两个输出之间进行refresh操作
session.refresh(news);
System.out.println(news);  // 这里打断点
}

 后台打印: 调用refresh方法, 虽然尝试发送了一条SELECT语句, 保证缓存中的数据和数据库的记录一致, 但是打印的还是数据库修改前的数据

 怎么获取session在执行过程中数据库实时修改后的最新值呢?

 解决方法: 设置数据库的隔离级别

--------------------------------------------

  简介:

  对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题: 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的. 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了. 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.

   数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题. 一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱

   数据库提供的 4 种事务隔离级别:

   

  Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED

  Mysql 支持 4 种事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ

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

  1. READ UNCOMMITED

  2. READ COMMITED

  4. REPEATABLE READ

  8. SERIALIZEABLE

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

-------------------------------------------

修改hibernate.cfg.xml, 设置mysql的事务隔离级别, 设成2(读与提交)

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

打断点测试: 在断点停住之后去数据库中修改记录, 这时候打印出来的就是数据库中最新的记录

结果: 

(1) 断点停住后后台打印:

(2) 修改数据库中的authod值, 改成Oracle

(3) 继续往下走, 打印的不再是SUN, 而是最新修改的Oracle

范例4: clear()

作用: 清理缓存

@Test
public void testSessionClear() {
News news = (News) session.get(News.class, 1);
// 清理缓存, 加上以下语句会发送两条SELECT语句
session.clear();
News news2 = (News) session.get(News.class, 1);
}

 

posted @ 2017-03-14 14:26  半生戎马,共话桑麻、  阅读(990)  评论(0)    收藏  举报
levels of contents