3.2 Hibernate的事務控制


1. 一級緩存

前面學習了一級緩存的主要兩個作用:

提高效率手段1:提高查詢效率
提高效率手段2:減少不必要的修改語句發送

現在開始了解一下Hibernate的事務控制。Hibernate是對JDBC的輕量級封裝,其主要功能是操作數據庫。在操作數據庫過程中,經常會遇到事務處理的問題,那么我們接下來就介紹hibernate中的事務管理。

回歸一下,什么是事務:在數據庫操作中,一項事務(Transaction)是由一條或多條操作數據庫的SQL語句組成的一個不可分割的工作單元。當事務中的所有操作都正常完成時,整個事務才能被提交到數據庫中,如果一項操作沒有完成,則整個事務會被回滾。

其實事務總結起來理解為:邏輯上的一組操作,組成這組操作的各個單元,要么一起成功,要么一起失敗。

事務的四個特性:事務有很嚴格的定義,需要同時滿足四個特性,即原子性、一致性、隔離性、持久性。這四個特性通常稱之為ACID特性,具體如下:

  原子性(Atomic):表示該事務中所做的操作捆綁成一個不可分割的單元,即對事務所進行的數據修改等操作,要么全部執行,要么全部不執行。

  一致性(Consistency):表示事務完成時,必須使所有的數據都保持一致狀態。

  隔離性(Isolation):指一個事務的執行不能被其它事務干擾。即一個事務內部的操作及使用的數據對並發的其他事務時隔離的,並發執行的各個事務之間不能互相干擾。

  持久性(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的操作其實還沒有查詢多條記錄。那如果我們需要查詢多條記錄要如何完成呢,我們接下去學習一下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM