3.2 Hibernate的事务控制

1. 一级缓存mysql

前面学习了一级缓存的主要两个做用:sql

提升效率手段1:提升查询效率
提升效率手段2:减小没必要要的修改语句发送
数据库

如今开始了解一下Hibernate的事务控制。Hibernate是对JDBC的轻量级封装,其主要功能是操做数据库。在操做数据库过程当中,常常会遇到事务处理的问题,那么咱们接下来就介绍hibernate中的事务管理。缓存

回归一下,什么是事务:在数据库操做中,一项事务(Transaction)是由一条或多条操做数据库的SQL语句组成的一个不可分割的工做单元。当事务中的全部操做都正常完成时,整个事务才能被提交到数据库中,若是一项操做没有完成,则整个事务会被回滚。安全

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

事务的四个特性:事务有很严格的定义,须要同时知足四个特性,即原子性、一致性、隔离性、持久性。这四个特性一般称之为ACID特性,具体以下:并发

  原子性(Atomic):表示该事务中所作的操做捆绑成一个不可分割的单元,即对事务所进行的数据修改等操做,要么所有执行,要么所有不执行。oracle

  一致性(Consistency):表示事务完成时,必须使全部的数据都保持一致状态。app

  隔离性(Isolation):指一个事务的执行不能被其它事务干扰。即一个事务内部的操做及使用的数据对并发的其余事务时隔离的,并发执行的各个事务之间不能互相干扰。dom

  持久性(Durability):持久性也叫永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。提交后的其余操做或故障不会对其有任何影响。

 

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

  1).脏读:一个事务读取到另外一个事务未提交的数据。

  2).不可重复读:一个事务读到了另外一个事务已经提交的update的数据,致使在同一个事务中的屡次查询结果不一致。

  2).虚读/幻读:一个事务读到了另外一个事务已经提交的insert的数据,致使在同一个事务中的屡次查询结果不一致。

 

事务的隔离级别

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

  1. 读未提交(Read Uncommitted,1级): 一个事务在执行过程当中,既能够访问其余事务提交的新插入的数据,又能够访问未提交的修改数据。若是一个事务已经开始写数据,则另一个事务则不容许同时进行写操做,但容许其余事务读此行数据。此隔离级别可防止丢失更新。

  2.已提交读(Read committed, 2级): 一个事务在执行过程当中,既能够访问其余事务成功提交的新插入的数据,又能够访问成功修改的数据。读取数据的事务容许其余事务继续访问该行数据,可是未提交的写事务将会禁止其余事务访问该行。此隔离级别可有效防止脏读。

  3.可重复读(Repeatable Read, 4级): 一个事务在执行过程当中,能够访问其余事务成功提交的新插入的数据,但不能够访问成功修改的数据。读取数据的事务将会禁止写事务(但容许读事务),写事务则禁止任何其余事务。此隔离级别可有效的防止不可重复读和脏读。

  4. 序列化/串行化(Serializable, 8级):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。此隔离级别可有效的防止脏读、不可重复读和幻读。

READ_UNCOMMITTED: 容许你读取还未提交的改变了的数据。可能致使脏、幻、不可重复读;

READ_COMMITTED: 容许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生;

REPEATABLE_READ: 对相同字段的屡次读取是一致的,除非数据被事务自己改变。可防止脏、不可重复读,但幻读仍可能发生。

SERIALIZABLE:彻底服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在全部的隔离级别中是最慢的,它是典型的经过彻底锁定在事务中涉及的数据表来完成的。

事务的隔离级别,是由数据库提供的,并非全部数据库都支持四种隔离级别:

  Mysql:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE(默认REPEATABLE_READ)

  Oracle: READ_UNCOMMITTED、READ_COMMITTED、SERIALIZABLE(默认 READ_COMMITTED)

在使用数据库时候,隔离级别越高,安全性越高,性能越低。

实际开发中,不会选择最高或者最低隔离级别,选择READ_COMMITTED(oracle默认)、REPEATABLE_READ(mysql默认)

 

2 Hibernate事务管理

在Hibernate中,能够经过代码来操做管理事务,如经过

“Transaction tx=session.beginTransactiong();”

开启一个事务,持久化操做后,经过"tx.commit();" 提交事务;若是事务出现异常,又经过“tx.rollback();"操做来撤销事务(事务回滚)。

  除了在代码中对事务开启,提交和回滚操做外,还能够在hibernate的配置文件中对事务进行配置。配置文件中,能够设置事务的隔离级别。其具体的配置方法是在hibernate.cfg.xml文件中的

<session-factory>标签元素中进行的。配置方法以下所示。

<!--

  事务隔离级别

  hibernate.connection.isolation = 4

  1-- Read uncommitted isolation

  2-- Read committed isolation

  4-- Repeatable read isolation

  8-- Serializable isolation

-->

<property name="hibernate.connection.isolation">4</property>

到这里咱们已经设置了事务的隔离级别,那么咱们在真正进行事务管理的时候,须要考虑事务的应用场景,也就是说咱们的事务控制不该该是在DAO层实现的,应该在Service层实现,而且在Service中调用多个DAO实现一个业务逻辑的操做。具体操做以下显示:

其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操做使用的是同一个Session对象。

其实有两种办法能够实现:

  1. 能够在业务层获取到Session,并将Session做为参数传递给DAO。

  2. 可使用ThreadLocal将业务层获取的Session绑定到当前线程中,而后再DAO中获取Session的时候,都从当前线程中获取。

其实使用第二种方式确定是最优方案,那么具体的实现已经不用咱们来完成了,hibernate的内部已经将这个事情作完了。咱们只须要完成一段配置便可。

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

    Session对象的生命周期与本地线程绑定

    Session对象的生命周期与JTA事务绑定

    Hibernate委托程序管理Session对象的生命周期

在Hibernate的配置文件中,hibernate.current_session_context_class属性用于指定Session管理方式,可选值包括:

  1. thread:Session对象的生命周期与本地线程绑定(推荐)

  2. jta:Session对象的生命周期与JTA事务绑定

  3. managed:hibernate委托程序来管理Session对象的生命周期。

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

<!-- 配置session绑定本地线程 -->

<property name="hibernate.current_session_context_class">thread</property>

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

在HibernateUtils工具类中更改getCurrentSession方法:

public static Session getCurrentSession() {

return sessionFactory.getCurrentSession();

}

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

因此最终的配置文件以下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd" >
 3 <hibernate-configuration>
 4     <session-factory>
 5         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
 6         <property name="hibernate.connection.url">jdbc:mysql:///day24_db</property>
 7         <property name="hibernate.connection.username">root</property>
 8         <property name="hibernate.connection.password">toor</property>
 9         <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
10         <property name="hibernate.show_sql">true</property>
11         <property name="hibernate.format_sql">true</property>
12         <property name="hibernate.hbm2ddl.auto">update</property>
13         <!--  配置C3P0链接池 
14         <property name="connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
15         在链接池中可用的数据库链接的最少数目
16         <property name="c3p0.min_size">5</property>
17         在链接池中全部数据库链接的最大数目
18         <property name="c3p0.max_size">20</property>
19         设定数据库链接的过时时间,以秒为单位,若是链接池中的某个数据库链接处于
20             空闲状态的时间超过了timeout时间,就会从链接池中清除
21         <property name="c3p0.timeout"></property>
22         每3000秒检查全部链接池中的空闲链接,以秒为单位
23         <property name="c3p0.idle_test_period">3000</property> -->
24         
25         <!-- 配置隔离级别 -->
26         <property name="hibernate.connection.isolation">4</property>
27         <!-- 配置session绑定本地线程 -->
28         <property name="hibernate.current_session_context_class">thread</property>
29         <mapping resource="cn/eagle/domain/Customer.hbm.xml" />
30     </session-factory>
31 </hibernate-configuration>
hibernate.cfg.xml

最终的工具类以下:

 1 package cn.eagle.utils;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.SessionFactory;
 5 import org.hibernate.cfg.Configuration;
 6 
 7 public class HibernateUtils {
 8 
 9     private static final Configuration configuration;
10     private static final SessionFactory sessionFactory;
11     
12     static {
13         configuration = new Configuration().configure();
14         sessionFactory = configuration.buildSessionFactory();
15     }
16     
17     public static Session getCurrentSession() {
18         return sessionFactory.getCurrentSession();
19     }
20 }
Customer.hbm.xml

到这里咱们已经对Hibernate的事务管理有了基本的了解,可是以前咱们所作的CRUD的操做其实尚未查询多条记录。那若是咱们须要查询多条记录要如何完成呢,咱们接下去学习一下。

相关文章
相关标签/搜索