一、概述

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

二、Session 缓存

1.简介

(1)Session 有一个缓存,称为 Hibernate 一级缓存。位于缓存中的对象称为持久化对象,每一个持久化对象与数据库中的一条记录对应。

(2)站在持久化的角度,Hibernate 将对象分为 4 种状态:临时状态、持久化状态、游离状态、删除状态。

2.测试 Session 缓存

(1)准备

①hibernate.cfg.xml 文件请参看上一篇文章。

②SessionFactory、Session、Transaction

private SessionFactory sessionFactory;
private Session session;
private Transaction transaction; @Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
} @After
public void destroy() {
transaction.commit();
session.close();
sessionFactory.close();
}

说明:使用单元测试类进行测试。因为是测试环境,不存在并发的情况,创建了一个 Session 对象。

(2)测试

@Test
public void testSession() {
News news = (News) session.get(News.class, 1);
System.out.println(news); News news2 = (News) session.get(News.class, 1);
System.out.println(news2); System.out.println(news.equals(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
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}
News{id=1, title='Title', author='tom', date=2016-09-28}
true

说明:

第一次查询的时候,会将引用赋值给 news,同时向 Session 缓存中存入了一份。

第二次查询的时候,并没有发送 select 语句,而是从 Session 缓存中直接获取的。

3.操纵 Session 缓存

(1)flush() :使数据表中的记录和 Session 缓存中的对象的状态保持一致。

① 在 Transaction 的 commit() 方法中,先调用 session 的 flush 方法,再提交事务。

org.hibernate.engine.transaction.spi.AbstractTransactionImpl#commit

@Override
public void commit() throws HibernateException {
if ( localStatus != LocalStatus.ACTIVE ) {
throw new TransactionException( "Transaction not successfully started" );
} LOG.debug( "committing" ); beforeTransactionCommit(); try {
doCommit();
localStatus = LocalStatus.COMMITTED;
afterTransactionCompletion( Status.STATUS_COMMITTED );
}
catch ( Exception e ) {
localStatus = LocalStatus.FAILED_COMMIT;
afterTransactionCompletion( Status.STATUS_UNKNOWN );
throw new TransactionException( "commit failed", e );
}
finally {
invalidate();
afterAfterCompletion();
}
}

org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction#beforeTransactionCommit

protected void beforeTransactionCommit() {
this.transactionCoordinator().sendBeforeTransactionCompletionNotifications(this);
if(this.isDriver && !this.transactionCoordinator().getTransactionContext().isFlushModeNever()) {
this.transactionCoordinator().getTransactionContext().managedFlush();
} if(this.isDriver) {
this.transactionCoordinator().getTransactionContext().beforeTransactionCompletion(this);
} }

② 可能会打印 SQL 语句,但是不会提交事务。

③ 在未提交事务或显式的调用 flush() 方法前,也可能会进行 flush() 操作。

  • 执行 HQL 或 QBC 查询,会先进行 flush() 操作,以得到数据表的最新记录。
  • 若记录的 ID 是由数据库使用的自增的方式生成的,则在调用 save() 方法时,就会立即发送 INSERT 语句,因为 save 方法后,必须保证对象的 ID 存在。

(2)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);
}

我在第5行断点,然后修改数据库中 News 的 `author` 字段,改为 jerry。执行。

两次打印结果相同。

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
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}
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
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}

原因:数据库的隔离级别,Mysql 默认隔离级别为 REPEATABLE READ。

在 Hibernate 的配置文件中可以显式的设置隔离级别. 每一个隔离级别都对应一个整数:

1. READ UNCOMMITED

2. READ COMMITED

4. REPEATABLE READ

8. SERIALIZEABLE

Hibernate 通过为 Hibernate 映射文件指定 hibernate.connection.isolation 属性来设置事务的隔离级别。
修改后的打印结果:

两次打印结果不同。

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
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='jerry', date=2016-09-28}
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
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}

(3)clear():清理缓存。

@Test
public void testClear() {
session.get(News.class, 1);
session.clear();
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_.date as date4_0_0_
from
hibernate.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_.date as date4_0_0_
from
hibernate.news news0_
where
news0_.id=?

三、Session API

1.四种状态的转换图

(1)临时对象

  • 在使用代理主键的情况下,OID 通常为 null
  • 不处于 Session 的缓存中
  • 在数据库中没有对应的记录

(2)持久化对象

  • OID 不为空
  • 位于 Session 缓存中
  • 在同一个 Session 实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象

(3)游离对象

  • OID 不为空
  • 不处于 Session 缓存中

(4)删除对象

  • 在数据库中没有和其 OID 对应的记录
  • 不再处于 Session 缓存中

2.save()

(1)将一个临时对象转变为持久化对象

(2)为对象分配 ID

(3)在 flush 缓存的时候,计划执行一条 INSERT 语句

(4)在 save() 方法前的 id 是无效的

(5)持久化对象的 ID 是不能被更改的。因为 Hibernate 通过持久化对象的 OID 来维持它与数据库相关记录的对应关系。

* persist() 和 save() 区别

对一个 OID 不为 Null 的对象执行 save() 方法时,会把该对象以一个新的 OID 保存到数据库中,而 persist() 则会抛出一个异常。

3.get()/load()

(1)都可以根据 OID 从数据库中加载一个持久化对象。

(2)执行 get() 时会立即加载对象。执行 load() ,若不使用该对象,则不会立即执行查询操作,而是返回一个代理对象。

(3)get() 是立即检索,而 load() 是延迟检索。

(4)若数据表中没有对应记录,Session 也没有被关闭。get() 返回 null,load() 使用返回对象时抛出异常。

(5)load() 可能会抛出 LozyInitizationException 异常:在需要初始化代理对象之前已经关闭了 Session。

@Test
public void testLoad() {
News news = (News) session.load(News.class, 1);
session.close();
System.out.println(news);
}
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

4.update()

(1)将一个游离对象转变为持久化对象,并且计划执行一条 update 语句。

(2)若更新一个持久化对象,不需要显式的调用 update() 方法。因为在调用 Transaction 的 commit() 方法时,会先执行 session 的 flush() 方法。

(3)注意

  • 无论要更新的游离对象和数据表的记录是否一致,都会发送 UPADATE 语句。如何让只在不一致的情况下发送 UPDATE 语句?在 entity.hbm.xml 文件的 class 节点设置                             select-before-update=true(默认为 false)。通常不需要设置,与触发器协同工作时需要注意。
  • 若数据表中没有对应的记录,但还是调用了 update() 方法,会抛出异常。
  • 当 update() 方法关联一个游离对象时,如果在 Session 缓存中已经存在相同 OID 的持久化对象,会抛出异常。因为在 Session 缓存中不能有两个 OID 相同的对象。

5.saveOrUpdate()

(1)同时包含了 save() 和 update() 方法的功能。

(2)判断是否是游离对象还是临时对象是根据 对象的 OID 来判定的。若为 null ,则执行 save() ,若不为 null,则判定为游离对象,执行 update() 。

(3)若 OID 不为 null,但数据中还没有与之对应的记录,则会抛出一个异常。

(4)了解:OID 值等于 id 的 unsaved-value 属性值的对象,也被认为是一个游离对象。

6.delete()

(1)既可以删除一个游离对象,也可以删除一个持久化对象。

(2)只要 OID 和数据表中一条记录对应,就会准备执行 delete 操作,若 OID 在数据表中没有对应的记录,则抛出异常。

(3)在执行 delete() 后,还是可以获取到对象的 OID,防止对该对象的其他持久化操作,可以通过设置 hibernate 配置文件的 hibernate.use_identifier_rollback 为 true,

使删除对象后,把其 OID 值为 null。

7.evict()

把指定持久化对象从 session 缓存中移除。

8.调用存储过程

@Test
public void testWork() {
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
System.out.println(connection);
// 调用存储过程
}
});
}

四、总结

介绍了 Hibernate 的一级缓存,包括如何操纵 Session 的缓存,以及四种状态之间的转换,以及建立在 Session 缓存和四种状态基础上的 Session API。

Hibernate —— Session的更多相关文章

  1. org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

    org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not a ...

  2. Use Spring transaction to simplify Hibernate session management

    Spring对Hibernate有很好的支持    DataSource ->SessionFactory-> HibernateTranscationManagerHibernate中通 ...

  3. Could not open Hibernate Session for transaction;

    javax.servlet.ServletException: org.springframework.transaction.CannotCreateTransactionException: Co ...

  4. Hibernate3回顾-5-简单介绍Hibernate session对数据的增删改查

    5. Hibernate对数据的增删改查 5.1Hibernate加载数据 两种:get().load() 一. Session.get(Class arg0, Serializable arg1)方 ...

  5. [转]Hibernate Session各种状态转换方法分析

    摘自http://spiritfrog.iteye.com/blog/221177 我的印象里, Hibernate session中常用的保存操作只有:save, update, saveOrUpd ...

  6. spring事务管理出错。No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy ...

  7. [原理][源代码解析]spring中@Transactional,Propagation.SUPPORTS,以及 Hibernate Session,以及jdbc Connection关系---转载

    问题: 一. 1. Spring 如何处理propagation=Propagation.SUPPORTS? 2. Spring 何时生成HibernateSession ? 3. propagati ...

  8. No Hibernate Session bound to thread, and configuration does not allow creat

    No Hibernate Session bound to thread, and configuration does not allow creat 今天遇到这么一个错误,在网上差了很多都没有能解 ...

  9. Hibernate Session 获取connection

    Hibernate Session 获取connection 由于最近一个项目要用到一条辅助的SQL ,hibernate里面的SQLQuery API 总的SQL语句不能包含 : 冒号, 固放弃Hi ...

  10. hibernate.Session简介

    ★→→SessionFactory (org.hibernate.SessionFactory) 包含已经编译的映射(mappings),是制造session的工厂,可能含有一些可以在各个事务(tra ...

随机推荐

  1. java入门第三步之数据库连接

    数据库连接可以说是学习web最基础的部分,也是非常重要的一部分,今天我们就来介绍下数据库的连接为下面学习真正的web打下基础 java中连接数据库一般有两种方式: 1.ODBC——Open Datab ...

  2. Linux下如何自己编译源代码(制作成可以安装的.deb文件)

    以tree实用程序(以树型结构获取目录树)为例,介绍Ubuntu中如何管理源码包,包括查询,获取,编译源码包,直至安装.   1) 在获取源码包之前,确保在软件源配置文件/etc/apt/source ...

  3. UI控件(UIButton)

    @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // UIButtonTypeCustom = 0, ...

  4. Lesson 5 No wrong numbers

    Text Mr.James Scott has a garage in Silbury and now he has just bought another garage in Pinhurst. P ...

  5. Nova PhoneGap框架 第四章 本地数据库

    我一直想把EntityFramework(简称EF)的那一套搬过来,应用于HTML5 SQLite. 幸运的是,我几乎做到了,有些功能无法完成的那是因为SQLite本身不支持.至少从现在已经完成的功能 ...

  6. Python黑帽编程2.5 函数

    Python黑帽编程2.5 函数 写了几节的基础知识,真心感觉有点力不从心.这块的内容说实话,看文档是最好的方式,本人的写作水平,真的是找不出更好的写法,头疼.简单带过和没写一样,写详细了和本系列教程 ...

  7. 剑指Offer面试题:28.连续子数组的最大和

    一.题目:连续子数组的最大和 题目:输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n).例如输入的数组为{1,-2,3 ...

  8. Redis系列(四)-低成本高可用方案设计

    关于Redis高可用方案,看到较多的是keepalived.zookeeper方案. keepalived是主备模式,意味着总有一台浪费着.zookeeper工作量成本偏高. 本文主要介绍下使用官方s ...

  9. Javascript模拟继承(赠送.net吐槽一段)

    首先吐槽一句,今年的就业形势很不乐观啊,特别是搞.net的(相对java),特特别是还没出校门没有正式工作经验的,找个实习很难,前些天接了个面试电话,上来就质疑我“你一个在校大学生怎么可能做了那么多项 ...

  10. 《Node即学即用》—— 读后总结

    导图