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的操做其實尚未查詢多條記錄。那若是咱們須要查詢多條記錄要如何完成呢,咱們接下去學習一下。

相關文章
相關標籤/搜索