一. Hibernate中的事务

1. 事务的回顾

1.1 什么是事务(Transaction)(面试重点)

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

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

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

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

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

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

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

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

1.2 为什么要使用事务?

  • 为了提高性能

  • 为了保持业务流程的完整性

  • 使用分布式事务

1.3 事务的特性

ACID

  • 原子性(atomicity)

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

  • 一致性(consistency)

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

  • 隔离性(isolation)

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

  • 持久性(durability)

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

1.4 事务的并发问题

  • 脏读(Dirty Read)

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

  • 不可重复读(虚读)(NonRepeatable Read)

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

  • 幻读(Phantom Read)

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

1.5 事务的隔离级别

  • 1- 读未提交

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

  • 2- 读已提交

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

  • 4- 可重复读

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

  • 8- 串行化读

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

2.Hibernate的事务隔离级别

2.1 配置

在Hibernate.cfg.xml中进行配置

<!-- 修复 hibernate 的隔离级别 -->
<property name="hibernate.connection.isolation">4</property>

可以配置四个值:

1: read uncommited

2: read commited

4: repeatable read

8: serializeable

3. 使用ThreadLocal管理Session(重点,记得会使用getCurrentSession)

3.1 事务管理案例

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

JavaDao代码:

 public class CustomerDao {
public void save(Customer cust){
Session session = HibernateUtil.openSession();//每次都拿到新的session
session.save(cust);
//不能关闭session
//session.close();
}
}

JavaService代码:

 public class CustomerService {
private CustomerDao dao = new CustomerDao();; public void save(Customer c1,Customer c2){
Session session = HibernateUtil.openSession();
//开启事务
Transaction tx = session.beginTransaction();
try {
dao.save(c1);
dao.save(c2);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
}
}

测试:

    /**
* 事务测试
*/
@Test
public void test2(){
Customer c1 = new Customer();
c1.setName("张三"); Customer c2 = new Customer();
c2.setName("李四"); CustomerService service = new CustomerService();
service.save(c1, c2);
}

3.2 解决方案

3.2.1 修改session的获取方式

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

Session session = HibernateUtil.getCurrentSession();

3.2.2 在hibernate.cfg.xml中配置

<!-- 让session被TheadLocal管理 -->
<property name="current_session_context_class">thread</property>

注意:

1.使用getCurrentSession时,增删改查操作都需要事务支持

2.getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

3.getCurrentSession创建的Session会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭

二. 更新数据丢失

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

  • 如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。

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

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

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

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

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

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

  • 悲观锁:

    采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句

    当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。

    只有当A事务提交后,锁释放了,其他事务才能操作该条记录

实现代码:

session.get(Customer.class, 1,LockMode.UPGRADE);  //操作数第三个参数添加锁
  • 乐观锁:

    采用版本号的机制来解决的。会给表结构添加一个字段version=0,默认值是0

    当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1

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

  1.在对应的JavaBean中添加一个属性,名称可以是任意的。
例如:private Integer version; 提供get和set方法
2.在映射的配置文件中,提供<version name="version"/>标签即可。
对比version 如果版本不是最新的 !那么操作不成功!
<!-- 就是实体实体类中version -->
<version name="version"></version>

三. 持久化类讲解

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等模式!

修改生成策略位置:

<!-- 配置主键id
name javaBean的属性
column 表结构的属性
如果相同可以去掉 column
-->
<!-- 主键生成策略,修改class值即代表修改主键生成策略 -->
<id name="cust_id" column="cust_id">
<generator class="native"/>
</id>

具体策略值:

  • increment:适用于short,int,long作为主键.不是使用的数据库自动增长机制。

    Hibernate中提供的一种增长机制.

    先进行查询 :select max(id) from user;

    再进行插入 :获得最大值+1作为新的记录的主键.

    问题:不能在集群环境下或者有并发访问的情况下使用.

  • identity:适用于short,int,long作为主键。但是这个必须使用在有自动增长数据库中.采用的是数据库底层的自动增长机制.底层使用的是数据库的自动增长(auto_increment).像Oracle数据库没有自动增长.

    所以此值mysql支持!

  • sequence:适用于short,int,long作为主键.底层使用的是序列的增长方式.Oracle数据库底层没有自动增长,想自动增长需要使用序列.

    此值Oracle支持!

  • uuid:适用于char,varchar类型的作为主键.

    使用随机的字符串作为主键.

  • native:本地策略.根据底层的数据库不同,自动选择适用于该种数据库的生成策略.(short,int,long)

    如果底层使用的MySQL数据库:相当于identity.

    如果底层使用Oracle数据库:相当于sequence.

  • assigned:主键的生成不用Hibernate管理了.必须手动设置主键.

  • 持久化对象的几种状态

  • 持久化对象状态转换

四. 持久化对象

1. 项目准备

1.1 创建项目

1.2 导入jar包

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

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

  • 建表语句

 CREATE TABLE `user`(
id integer primary key auto_increment,
name varchar(10) not null,
age integer,
version integer
)
  • 创建实体类
 public class User {
private Integer id;
private String name;
private Integer age;
private Integer version;
//getter setter toString
}
  • 创建持久化类映射文件   位置: 实体类相同文件夹   命名:User.hbm.xml
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置类和表的映射 catalog="" 数据库名称-->
<class name="com.itqf.bean.User" table="user" > <!-- 配置主键id
name javaBean的属性
column 表结构的属性
如果相同可以去掉 column
-->
<id name="id" column="id">
<!-- 主键生成策略 递增 -->
<generator class="native"/>
</id>
<!-- 就是实体实体类中version -->
<version name="version"></version> <!-- 其他的属性 -->
<property name="name" column="name" length="30"/>
<property name="age" column="age"/>
</class>
</hibernate-mapping>
  • 修改核心配置文件,添加User的映射文件
   <!-- 映射的 com开始-->
<mapping resource="/bean/Customer.hbm.xml"/>
<mapping resource="/bean/User.hbm.xml"/>

2. 持久化对象介绍

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

3. 持久化对象的三种状态(重点)

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

  • 瞬时态:Transient Object

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

  • 持久态:Persistent Object

    有持久化标识OID,已经被纳入到Session对象的管理.

  • 脱管态(游离态):Detached Object

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

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

展示持久化对象状态:

 @Test
public void testStatus(){ Session session = HibernateUtil.getSession(); Transaction beginTransaction = session.beginTransaction();
//持久化对象
User user = new User();
user.setName("王老五");
user.setAge(36); //----------- 以上是瞬时态 没有session管理没有 oid------------------
//返回值就是生成的id
Serializable id = session.save(user);
System.out.println(id);
beginTransaction.commit(); //------------ 以上是持久态,有session管理,有oid-----------
session.close(); System.out.println(user.getId());
System.out.println(user.getName());
//------------- 以上托管态, 有oid 但是没有session管理!-----------
}

测试自动更新功能:

  @Test
public void testAuto(){ Session session = HibernateUtil.getSession(); Transaction beginTransaction = session.beginTransaction(); User user = session.get(User.class, "8a8a200c5d7db0f7015d7db0fe280000");
user.setName("修改的name");
//看后台输出会发现,不用调用update方法,也会触发sql语句修改用户的name属性!
beginTransaction.commit();
session.close();
}

自动更新功能,其实是借助session的一级缓存!

3. 持久化对象状态转换

  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一级缓存中。

  • 一级缓存演示
 public class CacheLevelOneTest {

     /**
* 使用代码来证明Hibernate的一级缓存是存在的!
*/
@Test
public void testCache(){ Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); //第1次查询
Customer c1 = session.get(Customer.class, 1L);
System.out.println(c1); //第2次查询
Customer c2 = session.get(Customer.class,1L);
System.out.println(c2);
//第二次查询不触发sql语句,直接获取缓存中的结果!
tx.commit();
session.close();
}
}

2. Hibernate 的快照机制

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

1、快照是数据的副本

2、快照属于一级缓存

3、快照是在堆内存中的

4、快照的作用:保证数据一致性

 /**
* 说明持久态对象可以直接更新数据库的数据!
*/
@Test
public void testAutoUpdate(){ Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction(); //获取到一个持久态对象
Customer cust = session.get(Customer.class, 1L);
//修改cust的数据
cust.setName("汤姆"); //没有 必要执行update语句,因为现在持久态对象已经能够更新数据库的数据啦!
//session.update(cust); tx.commit();
session.close(); }

3. 一级缓存管理

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

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

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

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

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

 /**
* 一级缓存的管理
*/
@Test
public void testEvictAndClear(){
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
Customer cust = session.get(Customer.class, 1L); //cust是持久态对象,在一级缓存
cust.setName("老王"); //把cust对象移出一级缓存
session.evict(cust); //清空一级缓存
//session.clear(); tx.commit();
session.close();
}

Hibernate事务以及一级缓存02的更多相关文章

  1. hibernate中的一级缓存与闪照区

    首先Hibernate中的一级缓存默认是打开的,并且范围从session创建到session关闭,存储的数据必须是持久态的数据. 1 //从session创建开始,一级缓存也跟着创建 2 Sessio ...

  2. Hibernate的session一级缓存

    一级缓存是Session周期的,当session创建的时候就有,当session结束的时候,缓存被清空 当缓存存在的时候,每次查询的数据,都会放在缓存中,如果再次查询相同的数据,则不会再次查询数据库, ...

  3. Hibernate 二(一级缓存,多表设计之一对多)

    1       对象状态与一级缓存 1.1   状态介绍 l  hibernate 规定三种状态:瞬时态.持久态.脱管态 l  状态 瞬时态:transient,session没有缓存对象,数据库也没 ...

  4. Hibernate学习之一级缓存

    © 版权声明:本文为博主原创文章,转载请注明出处 Hibernate缓存: - 缓存是为了降低应用程序对物理数据源访问的频次,从而提供应用程序的运行性能的一种策略 - Hibernate缓存是提升和优 ...

  5. Hibernate中的一级缓存、二级缓存和懒加载(转)

    1.为什么使用缓存 hibernate使用缓存减少对数据库的访问次数,从而提升hibernate的执行效率.hibernate中有两种类型的缓存:一级缓存和二级缓存. 2.一级缓存 Hibenate中 ...

  6. Hibernate中的一级缓存、二级缓存和懒加载

    1.为什么使用缓存 hibernate使用缓存减少对数据库的访问次数,从而提升hibernate的执行效率.hibernate中有两种类型的缓存:一级缓存和二级缓存. 2.一级缓存 Hibenate中 ...

  7. Hibernate入门(四)---------一级缓存

    Hibernate一级缓存 Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对 ...

  8. Hibernate 再接触 一级缓存 二级缓存 查询缓存

    缓存 就是把本来应该放在硬盘里的东西放在内存里  将来存内存里读 一级缓存: session缓存 二级缓存: sessionFactory级别的   (适合经常访问,数据量有限,改动不大) 很多的se ...

  9. hibernate缓存(一级缓存、二级缓存)

    一.一级缓存(Session缓存)      意义:提高hibernate查询效率.      缺点:可能会因并发,产生数据不一致.      基于session的缓存,利用hibernate执行查询 ...

随机推荐

  1. [05] Bean的作用域和生命周期

    1.Bean的作用域和初始化时间 之前我们稍微提到过,Spring中管理的Bean,默认都是单例模式,这意味着你多次获取某个对象,得到的都是相同的对象.单例作用域的显性写法是scope属性,如下,这和 ...

  2. TCP/IP协议---IP协议

    IP协议层 在传输层的下边,链路层的上边. IP层封装后称为数据报(传输层叫数据段,链路层叫数据帧.值得注意的是,不是所有的以太网数据帧都是IP数据报,至少以太网还要使用ARP协议.不是所有的IP数据 ...

  3. 现有各种SSTC电路图,欢迎补充,研究,开发

    现有各种SSTC电路图,欢迎补充,研究,开发  496464505 2016-3-1 16:01:55 现在的各种SSTC基本都是这些图   2016-3-3 19:28:23 comter2001 ...

  4. ADO.NET分享

    看到<c#从入门到精通(第三版)>书中对ado.net里几种方法打的比方,很有意思就发一下. 1)这张图中,把数据比作水,储存了大量数据. 2)Connection对象好像进水龙头,插在水 ...

  5. WPF中的Bitmap与byte

    原文:WPF中的Bitmap与byte public MainWindow() { InitializeComponent(); byte[] b = GetPictureData(@"F: ...

  6. C#的RSA加密解密签名,就为了支持PEM PKCS#8格式密钥对的导入导出

    差点造了一整个轮子 .Net Framework 4.5 里面的RSA功能,并未提供简单对PEM密钥格式的支持(.Net Core有咩?),差点(还远着)造了一整个轮子,就为了支持PEM PKCS#8 ...

  7. ABP+AdminLTE+Bootstrap Table权限管理系统第六节--abp控制器扩展及json封装以及6种处理时间格式化的方法

    返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 一,控制器AbpController 说完了Swagger ui 我们再来说一下abp对控制器的处理和json的封 ...

  8. centos7.4下Jira6环境部署及破解操作记录(完整版)

    废话不多说,以下记录了Centos7针对Jira6的安装,汉化,破解的操作过程,作为运维笔记留存. 0) 基础环境 192.168.10.212 Centos7.4 mysql 5.6 jdk 1.8 ...

  9. apache工作模式总结及网站访问缓慢处理记录

    apache目前主要有两种模式:prefork模式和worker模式:1)prefork模式(默认模式)prefork是Unix平台上的默认(缺省)MPM,使用多个子进程,每个子进程只有一个线程.每个 ...

  10. Notes of Daily Scrum Meeting(12.24)

    平安夜了,我们还在这里辛苦地赶代码,整个人都不好了... 今天的团队任务总结如下: 团队成员 今日团队工作 陈少杰 寻找大神帮助,调试网络连接 王迪 建立搜索的UI界面,把算法加入进去 金鑫 调试网络 ...