博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HIbernate中的事务
阅读量:2442 次
发布时间:2019-05-10

本文共 4078 字,大约阅读时间需要 13 分钟。

什么是事务

在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被回滚。

其实事务总结起来理解为:逻辑上的一组操作,组成这组操作的各个单元,要么一起成功,要么一起失败。

事务的四个特性

事务由很严格的定义,需要同时满足四个特性,即原子性、一致性、隔离性、持久性。这四个特性通常称之为ACID特性,具体如下:

  • 原子性(Atomic):
    表示将事务中所做的擦做捆绑成一个不可分割的单元,即对事务所进行的数据修改等操作,要么全部执行,要么全都不执行。
  • 一致性(Consistency):
    表示事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):
    指一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability):
    持久性也成为永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。提交后的其他操作或故障不会对其由任何影响。

事务的并发问题

在实际应用过程中,数据库是要被多个用户所共同访问的。在多个事务同时使用相同的数据时,可能会发生并发的问题,具体如下:

  1. 脏读: 一个事务读取到另一个事务未提交的数据。
  2. 不可重复读: 一个事务读到了另一个事务提交的update的数据,导致在同一个事务中的多次查询结果不一致。
  3. 虚读/幻读: 一个事务读到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致。

事务的隔离级别

为了避免事务并发问题的发生,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。

  • 读未提交(Read Uncommitted,1级):
    一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。
  • 已提交读(Read Committed,2级):
    一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。
  • 可重复读(Repeatable Read,4级):
    一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。此隔离级别可有效防止不可重复读和脏读。
  • 序列化/串行化(Serializable, 8级):
    提供严格的事务隔离。它腰子u事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。此隔离级别可有效的防止脏读、不可重复读和幻读。
隔离级别 含义
READ_UNCOMMITTED 允许读取还未提交的改变了的数据。可能导致脏、幻、不可重复读。
READ_COMMITTED 允许在并发事务已经提交后读取、可防止脏读,但幻读和不可重复读仍可发生。
REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读然可能发生。
SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中设计的数据表来完成的。

事务的隔离级,是由数据库提供的,并不是所有数据库都支持四种隔离级别。

  • MySQL:
    READ_UNCOMMITTED、READ_COMMITTED、
    REPEATABLE_READ、READ_COMMITTED(默认REPEATABLE_READ)。
  • Oracle:
    READ_UNCOMMITTED、READ_COMMITTED、READ_COMMITTED(默认READ_COMMITTED)。

Hibernate中的事务管理

在Hibernate中,可以通过代码来操作管理事务,如通过“Transaction tx = session.beginTransaction();” 开启一个事务:持久化操作后,通过“tx.commit();” 提交事务;如果事务出现异常,又通过“tx.rollback();” 操作来撤销事务(事务回滚)。

除了在代码中对事务开启,提交和回滚操作外,还可以在Hibernate的配置文件中对事务进行配置。配置文件中,可以设置事务的隔离级别。具体的配置方法是在hibernate.cfg.xml 文件中的 标签元素中进行的。如下:

4

我们在真正进行事务管理的时候,需要考虑事务的应用场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个DAO实现一个业务逻辑的操作。

具体操作如下显示:

在这里插入图片描述

其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操作使用的是同一个Session对象。
其实由两种办法可以实现:

  • 可以在业务层获取到Session,并将Session作为参数传递给DAO。
  • 可以使用ThreadLocal 将业务层获取的Session绑定到当前线程中,然后在DAO中获取Session的时候,都从当前线程中获取。

其实使用第二种方式肯定是最优方案,那么具体的实现已经不用我们来完成了,Hibernate的内部已经帮我们将这个事情做完了。我们只需要完成一段配置即可。

Hibernate5 中自身提供了三种管理Session对象的方法:

  • t h r e a d : S e s s i o n 对 象 的 生 命 周 期 与 本 地 线 程 绑 定 。 \color{red}{thread:Session对象的生命周期与本地线程绑定。} threadSession线
  • jta:Session 对象的生命周期与 JTA 事务绑定。
  • managed:Hibernate委托程序来管理Session对象的生命周期。

在hibernate.cfg.xml 中进行如下配置:

thread

Hibernate提供sessionFactory.getCurrentSession() 创建一个session 和ThreadLocal 绑定方法。

书写一个HibernateUtils工具类:

package pers.zhang.utils;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;public class HibernateUtils {
private static SessionFactory sf; static{
//1 创建,调用空参构造 Configuration conf = new Configuration().configure(); //2 根据配置信息,创建 SessionFactory对象 sf = conf.buildSessionFactory(); } //获得session => 获得全新session public static Session openSession(){
//3 获得session Session session = sf.openSession(); return session; } //获得session => 获得与线程绑定的session public static Session getCurrentSession(){
//3 获得session Session session = sf.getCurrentSession(); return session; } }

而且Hibernate中提供的这个与线程绑定的session可以不用关闭,当线程执行结束后,就会自动关闭了。

测试方法:

package pers.zhang.test;import org.hibernate.Session;import org.junit.Test;import pers.zhang.utils.HibernateUtils;//测试getCurrentSessionpublic class Demo {
@Test //返回同一个与线程绑定的session public void fun1(){
Session session1 = HibernateUtils.getCurrentSession(); Session session2 = HibernateUtils.getCurrentSession(); System.out.println(session1==session2);//true } @Test //返回不同的session public void fun2(){
Session session1 = HibernateUtils.openSession(); Session session2 = HibernateUtils.openSession(); System.out.println(session1==session2);//false } }

运行JUnit测试输出:

truefalse

转载地址:http://fjsqb.baihongyu.com/

你可能感兴趣的文章
BigCommerce和Volusion:比较设置和移动
查看>>
使用codeigniter_使用CodeIgniter探索面向方面的编程,第2部分
查看>>
用Wufoo创建简单表单:开发人员的观点
查看>>
heroku_使用Heroku和Facebook SDK在云中构建您的应用
查看>>
SitePoint播客#145:Addy Osmani的Backbone.js基础
查看>>
Google发布Chrome 4.0
查看>>
将ip保存在txt文档中_将任何文件存储在Google文档中
查看>>
disconf apps_Google Apps Drop IE6支持
查看>>
使用Screenr创建截屏视频
查看>>
微信小程序使用flex布局_使用Flex来启动您PHP应用程序!
查看>>
互联网总线带宽_新技术将使互联网带宽翻倍
查看>>
java自由职业者是什么_一个成功的自由职业者需要什么?
查看>>
java语言作为入门好吗_Java作为学生的第一语言
查看>>
sql内联接外联接三张表_在SQL中联接三个或更多表
查看>>
企业为什么需要it服务?_为什么要考虑为您的企业提供IT支持服务?
查看>>
Django博客教程–创建一个简单的博客
查看>>
操作系统的主要类型_操作系统类型
查看>>
编程和编码的区别_安全编程和编码提示
查看>>
实用的小应用_使您的新应用更安全的实用提示
查看>>
小学生c语言编程入门教程_学生编程语言
查看>>