一、概述

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. 报文解析及CRC类

    /// <summary> /// 报文解析转换类 /// </summary> public class DatagramConvert { public static En ...

  2. 【实战Java高并发程序设计 5】让普通变量也享受原子操作

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  3. Mpale 在汽车底盘悬架系统公差分析应用

    汽车底盘的作用是接受发动机的动力,使车轮转动,并保证汽车按驾驶员的操纵正常行驶.底盘包括传动系统.行驶系统.转向系统和制动系统这四大部分,通常,这四大系统也简称为传动系.行驶系.转向系和制动系.悬架是 ...

  4. 用jdbc访问二进制类型的数据

    package it.cast.jdbc; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; impor ...

  5. shell脚本删除指定mobileprovision

    由于某种原因,xcode帮我按照了几千个开发和上线证书,需要删除这部分证书: #dir="/Users/Ethan/Library/MobileDevice/Provisioning Pro ...

  6. ORM数据层框架的设计热点:更新指定的列的几种设计方案

    ORM框架的定义:对象-关系映射(Object/Relation Mapping,简称ORM) 常见的是:数据库结构=>映射Object(实体属性)=>基于实体类的操作. 还有一种:数据库 ...

  7. .NET Core与.NET Framework、Mono之间的关系

    随着微软的.NET开源的推进,现在在.NET的实现上有了三个.NET Framework,Mono和.NET Core.经常被问起Mono的稳定性怎么样,后续Mono的前景如何,要回答这个问题就需要搞 ...

  8. Hadoop学习笔记—13.分布式集群中节点的动态添加与下架

    开篇:在本笔记系列的第一篇中,我们介绍了如何搭建伪分布与分布模式的Hadoop集群.现在,我们来了解一下在一个Hadoop分布式集群中,如何动态(不关机且正在运行的情况下)地添加一个Hadoop节点与 ...

  9. [异常解决] ubuntu上安装JLink驱动遇到的坑及给后来者的建议

    一.前言 最近将整个电脑格式化,改成了linux操作系统 希望这样能让自己在一个新的世界探索技术.提升自己吧- win上的工具用多了,就不想变化了- 继上一篇<ubuntu上安装虚拟机遇到的问题 ...

  10. Atitit 设计模式的本质思考】

    Atitit 设计模式的本质思考] 1. 世界就是有模式构建的1 1.1. 多次模式与偶然模式1 1.2. 模式就是在一种场合下对某个问题的一个解决方案."1 1.3. 模式需要三样东西.  ...