Hibernate事务管理

Hibernate事务管理

学习hibernate的事务管理前首先学习一下什么是事务?事务中存在哪些问题?如何解决的?这些都是事务的基础知识,其后才是hibernate的事务管理设置;

事务基础

什么是事务

事务在Java中就是指一个逻辑上的一组操作,组成这个逻辑的逻辑单元要么全部完成,要么全部失败;

事务的特性

1) 原子性:代表事务是不可分割的,要么同时完成要么同时失败,存在例外;

2) 一致性:代表数据执行前后数据保持一致,例如:甲乙买卖,最终两个人的钱和货物相加还是原始量,不会有不明下落的数据;

3) 隔离性:意思是指一个事物的执行过程中不应该和其他的事务有交集;

4) 持久性:代表事务执行完后,数据就会被持久存放到数据库中,不会被执行过的事务所调用;

在事务的特性中重点需要了解的是事物的隔离性,下面进行详细了解;

如果不考虑事务的隔离,会导致的问题:

隔离的目的是为了解决不同的事务同时并发的情况所会存在的问题,防止事务间的相互影响;如果不考虑事务的隔离,则会引发多种问题,总结为两大类,分别是读的问题和写的问题;

读的问题:

1)脏读:是指事物读B到事务A未提交的数据 — 既是:事务B读到的是事务A可能要提交也可能要回滚的数据;

2)不可重复度:是指事务A在进行操作数据并且读取到了数据,而此时事务B突然插队进来也操作数据,并且在A事务提交前进行了事务提交,则会导致事务A读取前后的数据不一致和操作不成功;

3)虚读(幻读):事务A开始操作数据库,把表中数据全部修改成某种样式的,但与此同时,事务B也在修改数据表,数据B的是添加一条原始样式的数据,在两个人都提交后,表中只有一条数据是原始样式的,当事务A回头查看时会发现又一条数据没有修改成新样式;

注意:这个幻读和不可重复度很容易搞混;

辨析:幻读是指之前查询不存在的,中途被其他事务添加了,再次查询却出现了,而不可重复度是指之前查询存在的,中途被其他事务修改了,再次查询却不同了;区别点就在于第一个事务第一次查询是否存在这个数据;

写的问题(了解即可)

1) 引发丢失更新

A事务在回滚时把B事务已经提交的更新数据也给回滚了(当然也有可能是A事务提交后覆盖了B事务的更新操作,导致需要的事务B操作的丢失了更新的结果,不一定非要是回滚,只是回滚更方便理解)!

事务的解决 — 事务的隔离级别

设置事务隔离级别(4种):

隔离级别名称隔离读取级别解决问题级别

Read uncommitted

读取未提交的数据

以上的读问题都会发生

Read committed

读取已提交的数据

除脏读其他均可能发生

Repeatable read

读取到已经修改过的

只有幻读可能会发生

Serializable

读问题都可以解决

串行化序列执行(例:排队上厕所,一个出来后另一个才能用)效率太低

隔离级别处理问题能力表:

Oracle使用的是 read committed 的隔离级别来配置事务管理;

MySQL使用的是 repeatable read 的隔离级别来配置事务管理;

事务进阶 — hibernate中设置隔离级别

上面回顾了不隔离导致的问题以及各个隔离级别的作用范围等,现在学习在hibernate中如何设置隔离级别;

hibernate中设置事务的隔离级别

首先,要知道hibernate中设置事务的隔离级别是在核心配置文件中进行的,其次,所使用的标签以及标签所对应的属性值,具体介绍如下:

在核心文件配置中的标签内进行定义的,所使用的方法举例为4

全部代码如下:

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

org.hibernate.dialect.MySQLDialect

com.mysql.jdbc.Driver

jdbc:mysql:///hibernate_day02

root

root

true

true

update

4

配置标签、属性什么的都好说,只要记住就好,主要注意的是标签中间的数字,例中是4,还有其他的1,2,8;这些数字代表的是其对应的隔离级别,具体介绍如下:

隔离级别对应数值隔离级别名称备注

1

read uncommitted

2

read committed

Oracle使用的隔离级别

4

repeatable read

MySQL使用的隔离级别

8

serializable

以上就是核心配置文件中的配置方法,配置完核心配置中的后需要使用,具体的是service层在使用;

Service层事务

事务管理等是在Service层的,事务层 = Service层;

为什么要把事务加在Service层?

答:三层架构,Web层、Service层、Dao层;Dao层里封装的是一个个对数据源单一操作的方法,每个对数据的增删改查的业务要分为四个方法,每个方法都要重新创建一个单独的连接(Hibernate中的是Session,JDBC中的是Connection),这样不便于业务逻辑的管理;而在service层中封装的是一个业务逻辑操作(最少两个的对数据源操作的Dao层方法,就比如转账的业务逻辑,由转出方扣钱和转入方加钱的业务统一成一个业务逻辑),service层便于此业务逻辑的事物管理;

回顾事务管理在service层中:

Service层事务管理管理的是一个业务逻辑,其中连接Dao层的多个对数据源单一操作的方法,这样的话就需要使用同一个连接,这个连接在Hibernate中就是Session。

在JDBC中使用的有两种方法,一种是在Service层创建完后传递给Dao层,Dao层的进行调用创建好的同一个连接,第二种是使用ThreadLocal(线程本地变量/线程本地储存)对象,这个对象的原理是将连接保存到线程中,通过线程来传递连接到Dao层;

Hibernate中的事务管理:

在Hibernate框架中提供的有已绑定好的ThreadLocal,存在于SessionFactory中,方法名叫做getCurrentSession(),此方法默认是关闭的,需要手动设置打开,操作方法:

1) 工具类:向生成一般的Session对象所在的工具类中添加可以生成getCurrentSession()方法;添加部分如下所示:

package com.java.hibernate.Utils;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.cfg.Configuration;

/*

* hibernate生成session的工具类

*/

public class HibernateUtils {

public static final Configuration conf;

public static final SessionFactory sf;

static{

//加载读取核心配置文件对象

conf = new Configuration().configure();

//使用核心配置文件对象创建Session工厂,生成链接session连接对象

sf = conf.buildSessionFactory();

}

public static Session openSession(){

return sf.openSession();

}

//添加 --- 生成线程session代码

public static Session getCurrentSession(){

return sf.getCurrentSession();

}

}

2) 配置允许使用getCurrentSession()方法的操作;在核心配置文件中进行配置,配置如下所示:

thread

其属性有三个,分别是:

(1) Thread:Session对象生命周期与本地线程绑定!!!

(2) jta:Session对象生命周期与JTA事务绑定(跨数据库事务);

(3) managed: Hibernate委托程序来管理session生命周期;

3) 测试代码:

@Test

public void demo1(){

Session session = HibernateUtils.getCurrentSession();

Transaction bt = session.beginTransaction();

Customer cust = session.load(Customer.class, 1l);

System.out.println(cust);

bt.commit();

}

能通过就算成功

注意:在使用此方法进行操作时后不需要有释放资源的操作(即:session.close()),因为线程会自动关闭一次,如果自己再手动关闭Session就会报错;

《本章完》

友情链接