Hibernate事务以及一级缓存

Hibernate事务以及一级缓存回顾任务目标一. Hibernate中的事务1. 事务的回顾1.1 什么是事务(Transaction)1.2 为什么要使用事务?1.3 事务的特性1.4 事务的并发问题1.5 事务的隔离级别2.Hibernate的事务隔离级别2.1 配置3. 使用ThreadLocal管理Session3.1 事务管理案例3.2 解决方案3.2.1 修改session的获取方式3.2.2 在hibernate.cfg.xml中配置二. 更新数据丢失1. 什么是更新数据丢失?2. 更新数据丢失解决方案三. 持久化类讲解1. 什么是持久化类?2. 持久化类编写规则3. 自然和代理主键4. 主键生成策略四. 持久化对象1. 项目准备1.1 创建项目 hibernate-02-optimize2. 持久化对象介绍3. 持久化对象的三种状态3. 持久化对象状态转换五. Hibernate的一级缓存1. 一级缓存介绍2. Hibernate 的快照机制3. 一级缓存管理课前默写作业面试题

回顾

1. Hibernate配置文件的使用
2. Hibernate基本使用步骤
3. 事务的基本作用

任务

1. Hibernate中事务详解
2. 更新数据丢失问题
3. Hibernate中的主键策略
4. Hibernate对象的三种状态
5. Hibernate的一级缓存

目标

1. 掌握Hibernate中事务详解
2. 掌握更新数据丢失问题
3. 掌握Hibernate中的主键策略
4. 掌握Hibernate对象的三种状态
5. 掌握Hibernate的一级缓存

一. Hibernate中的事务

1. 事务的回顾
1.1 什么是事务(Transaction)

是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,sql 能将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性。事务通常是以begin transaction开始,以commit或rollback结束。Commint表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据的更新写回到磁盘上的物理数据库中去,事务正常结束。Rollback表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态。

设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:

1)更新客户所购商品的库存信息

2)保存客户付款信息--可能包括与银行系统的交互

3)生成订单并且保存到数据库中

4)更新用户相关信息,例如购物数量等等

正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。

数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术

1.2 为什么要使用事务?
1.3 事务的特性

ACID

事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。

一个事务的执行不能被其他事务所影响。企业级的数据库每一秒钟都可能应付成千上万的并发访问,因而带来了并发控制的问题。由数据库理论可知,由于并发访问,在不可预料的时刻可能引发如下几个可以预料的问题:

一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改

1.4 事务的并发问题

一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被回滚。

一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。

事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据,这是因为在两次查询过程中有另外一个事务插入数据造成的

1.5 事务的隔离级别

Read uncommitted:最低级别,以上情况均无法保证。

Read committed:可避免脏读情况发生。(Oracle默认)

Repeatable read:可避免脏读、不可重复读情况的发生。不可以避免虚读。(MySQl默认)

Serializable:事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重.

2.Hibernate的事务隔离级别
2.1 配置

在Hibernate.cfg.xml中进行配置

 

可以配置四个值:

1: read uncommited

2: read commited

4: repeatable read

8: serializeable

3. 使用ThreadLocal管理Session
3.1 事务管理案例

注意:下面的测试无法保存数据,因为使用 openSession()方法拿到的都是独立的session对象,事物中提交的session并不是dao中操作的session.

JavaDao代码:

 

JavaService代码:

 

测试:

 
3.2 解决方案
3.2.1 修改session的获取方式

将dao层和service层中需要用到session的地方使用getCurrentSession()

 

3.2.2 在hibernate.cfg.xml中配置
 

二. 更新数据丢失

1. 什么是更新数据丢失?

​ 例如:两个事务同时对某一条记录做修改,就会引发丢失更新的问题。

​ A事务和B事务同时获取到一条数据,同时再做修改

​ 如果A事务修改完成后,提交了事务

​ B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响

两个同时更新! 第一次更新别第二次更新的覆盖了!!

2. 更新数据丢失解决方案

实现代码:

 

​ 当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。

 

三. 持久化类讲解

1. 什么是持久化类?

​ 持久化类:是指其实例需要被 Hibernate 持久化到数据库中的类。持久化类符合JavaBean的规范,包含一些属性,以及与之对应的 getXXX()setXXX() 方法。

2. 持久化类编写规则
  1. get/set方法必须符合特定的命名规则,get 和set 后面紧跟属性的名字,并且属性名的首字母为大写。
  2. name 属性的 get 方法为 getName(),如果写成 getname() 或 getNAME() 会导致 Hibernate 运行时抛出以下异常:net.sf.hibernate.PropertyNotFoundException:Could not find a getter for porperty name in class mypack XXX
  3. 如果属性为 boolean 类型,那么 get 方法名即可以用 get 作为前缀,也可以用 is 作为前缀。
  4. 持久化类必须有一个主键属性,用来唯一标识类的每一个对象。这个主键属性被称为对象标示符(OID,Object Identifier)。
  5. Hibernate要求持久化类必须提供一个不带参的默认构造方法,在程序运行时,Hibernate 运用Java反射机制,调用java.Lang.raflect.Constructor.newInstance()方法来构造持久化类的实例。
  6. 使用非final类。在运行时生成代理是 Hibernate 的一个重要功能。如果持久化类没有实现任何接口的话,Hibernate使用CGLIB生成代理,该代理对象时持久化类子类的实例。如果使用了final类,将无法生成CGLIB代理。还有一个可选的策略,让 Hibernate 持久化类实现一个所有方法都声明为public的接口,此时将使用JDK的动态代理。同时应该避免在非final类中声明public final的方法。如果非要使用一个有public final的类,你必须通过设置lazy=”false“来明确地禁用代理
3. 自然和代理主键

持久化类中必须包含一个主键属性,主键通常分为两种,自然和代理!

4. 主键生成策略

​ hibernate框架可以有效的帮助我们生成数据主键,可以是自增长,也可以是UUID等模式!

修改生成策略位置:

 

具体策略值:

四. 持久化对象

1. 项目准备
1.1 创建项目 hibernate-02-optimize

1.2 导入jar包

1.3 复制上个项目实体(客户),映射,配置和工具类等!

额外添加一个用户表,和实体类!

2. 持久化对象介绍

持久化类创建的对象就是持久化对象!

3. 持久化对象的三种状态

Hibernate为了管理持久化对象:将持久化对象分成了三个状态

持久化对象中,持久态最为重要,因为持久太对象具有自动更新功能!

展示持久化对象状态:

 

测试自动更新功能:

 

自动更新功能,其实是借助session的一级缓存!一级缓存后面进行讲解!

3. 持久化对象状态转换

1507281934175

  1. 瞬时态 -- 没有持久化标识OID, 没有被纳入到Session对象的管理

    ​ 获得瞬时态的对象

    ​ User user = new User()

    • 瞬时态对象转换持久态

      • save()/saveOrUpdate();
    • 瞬时态对象转换成脱管态

      • user.setId(1)
  2. 持久态-- 有持久化标识OID,已经被纳入到Session对象的管理

    ​ 获得持久态的对象

    ​ get()/load();

    • 持久态转换成瞬时态对象

      • delete(); --- 比较有争议的,进入特殊的状态(删除态:Hibernate中不建议使用的)
    • 持久态对象转成脱管态对象

      • session的close()/evict()/clear();
  3. 脱管态-- 有持久化标识OID,没有被纳入到Session对象的管理

​ 获得托管态对象:不建议直接获得脱管态的对象.

​ User user = new User();

​ user.setId(1);

​ 脱管态对象转换成持久态对象

​ update();/saveOrUpdate()/lock();

​ 脱管态对象转换成瞬时态对象

​ user.setId(null);

五. Hibernate的一级缓存

1. 一级缓存介绍

​ Hibernate的一级缓存是指Session(属于事务范围的缓存,由Hibernate管理,无需干预),它是一块内存空间,用来存放从数据库查询出的java对象,有了一级缓存,应用程序可以减少访问数据库的次数,提高了性能。

在使用Hibernate查询对象的时候,首先会使用对象属性的OID值(对应表中的主键)在Hibernate的一级缓存进行查找,如果找到,则取出返回,不会再查询数据库,如果没有找到,再到数据库中进行查询操作。然后将查询结果存放到Session一级缓存中。

1507282999231

2. Hibernate 的快照机制

当执行 commit() 时,Hibernate同时会执行 flush() 方法,hibernate会清理session的一级缓存(flush),也就是将堆内存中的数据与快照中的数据进行对比,如果不一致,则会执行同步(update)操作,若相同,则不执行update。

1、快照是数据的副本 2、快照属于一级缓存 3、快照是在堆内存中的 4、快照的作用:保证数据一致性

 
3. 一级缓存管理

Q:如果持久态对象不在一级缓存中,可以更新数据库吗?

A:不能!

把对象移出一级缓存的方法:

session.evict(object) : 把一个对象移出一级缓存

session.clear() : 把一级缓存的所有对象移出

测试:以下测试数据不会被更新

 

课前默写

1. Hibernate的配置文件
2. Hibernate操作的基本步骤  

作业

 

面试题

1. Hibernate中事务的特性
2. Hibernate中事务的传播性
3. Hibernate中的脏读、幻读、不可重复读
4. Hibernate中的悲观锁和乐观锁
5. Hibernate主键生成策略
6. Hibernate对象的三种状态
7. Hibernate的缓存