1. 程式人生 > >Hibernate之事務與併發控制

Hibernate之事務與併發控制

一,資料庫事務簡介

1.概念(我百度的)

資料庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。

事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。

通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢復並使應用程式更加可靠。

一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和永續性)屬性。

事務是資料庫執行中的一個邏輯工作單位,由DBMS中的事務管理子系統負責事務的處理。

2.事務的屬性

原子性(Atomic):

事務必須是原子工作單元;對於其資料修改,要麼全都執行,要麼全都不執行。

通常,與某個事務關聯的操作具有共同的目標,並且是相互依賴的。

如果系統只執行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統處理操作子集的可能性。

一致性(Consistency):

事務在完成時,必須使所有的資料都保持一致狀態。在相關資料庫中,所有規則都必須應用於事務的修改,以保持所有資料的完整性。

事務結束時,所有的內部資料結構(如 B 樹索引或雙向連結串列)都必須是正確的。

某些維護一致性的責任由應用程式開發人員承擔,他們必須確保應用程式已強制所有已知的完整性約束。

例如,當開發用於轉帳的應用程式時,應避免在轉帳過程中任意移動小數點。

隔離性(Isolation):

由併發事務所作的修改必須與任何其它併發事務所作的修改隔離。

事務檢視資料時資料所處的狀態,要麼是另一併發事務修改它之前的狀態,要麼是另一事務修改它之後的狀態,事務不會檢視中間狀態的資料。

這稱為隔離性,因為它能夠重新裝載起始資料,並且重播一系列事務,以使資料結束時的狀態與原始事務執行的狀態相同。

當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可並行執行的事務獲得的結果與通過連續執行每個事務所獲得的結果相同。

由於高度隔離會限制可並行執行的事務數,所以一些應用程式降低隔離級別以換取更大的吞吐量。

永續性(Duration):

事務完成之後,它對於系統的影響是永久性的。該修改即使出現致命的系統故障也將一直保持。

二,資料庫併發問題歸類

1.第一類丟失更新

撤銷一個事務時,把其他事務提交的更新資料給覆蓋了

2.第二類丟失更新

一個事務覆蓋另一個事務提交的更新

3.髒讀

一個事務讀到另一個事務未提交的資料

4.虛讀

一個事務讀到另一個事務插入提交的資料

5.不可重複讀

一個事務讀到另一個事務已提交更新的資料

三,資料庫系統鎖的原理

當一個事務訪問資料庫資源時,比如執行select必須獲得共享鎖,執行insert,update,delete必須獲得獨佔鎖,

所有的這些所都是用於安全的操縱資料庫資源。

當第二個事務訪問資料庫資源時,通過鎖來判斷是否獲取資源,是否等待鎖的釋放,獲取資料資源。

鎖的多粒度性和自動升級:

資料庫鎖能夠鎖定的範圍很廣,比如:資料庫,表,行等。

按照鎖定資源的粒度分為以下型別的鎖:

.資料庫級別鎖:鎖定整個資料庫

.表級別鎖:鎖定一張表

.行級鎖:鎖定一行資料

等等。

按照封鎖程度,分為共享鎖,獨佔鎖,更新鎖:

共享鎖:

共享鎖用於讀取資料的操作,允許其他事務同時讀取資料庫資源,不允許其他事務更新,只讀不寫。

獨佔鎖(排他鎖):

用於更新資料,其他事務不能讀,也不能修改,不允許別人讀寫。

更新鎖:

更新操作的初始化階段用於鎖定資源,避免這個時候使用共享鎖造成死鎖。

四,資料庫事務的隔離級別

序列化(Serializable):

隔離級別最高,一個事務在執行過程中完全看不到其他事務對資料庫做的操作。一心只讀聖賢書,兩耳不聞窗外事。

可重複讀(Repeatable Read):

一個事務在執行過程中可以看到另外一個事務插入的新資料,但是看不到別人對已有資料的更新。

讀已提交資料(Read Committed):

一個事務在執行過程中可以看到另外一個事務插入的新資料,而且能看到別人對已有資料的更新。

讀未提交資料(Read Uncommitted):

隔離級別最差,一個事務在執行過程中可以看到另外一個事務插入的新資料,而且能看到別人對已有資料的更新,但是未提交的資料。

在Hibernate的配置檔案中可以顯示地設定隔離級別。每一種隔離級別對應著一個正整數。

Read Uncommitted: 1 Read Committed: 2 Repeatable Read: 4 Serializable: 8 在hibernate中hibernate.cfg.xml檔案對隔離的配置:

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

五,悲觀鎖和樂觀鎖

悲觀鎖:

應用程式顯示的載入資料庫資源。悲觀鎖假定當前事務操作資料時,會有別的事務來搶,乾脆就把這條資料鎖了,別人沒戲。效能差,慎用。

sql中可以用select...for update鎖定資料,而Hibernate中用程式碼控制轉換:

package com.lanhuigu.hibernate.test;

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import com.lanhuigu.hibernate.entity.Customer;

public class TestHibernate {
	public static void main(String[] args) {
		Configuration configuration = new Configuration().configure();
		SessionFactory sessionFactory = configuration.buildSessionFactory();
		Session session = sessionFactory.openSession();
		Transaction tr = session.beginTransaction();
		//悲觀鎖使用
		Customer customer = (Customer) session.get(Customer.class, new Long(1),LockMode.UPGRADE);
		System.out.println(customer.getName());
		tr.commit();
		session.close();
	}
}

控制檯sql後多一個for update:

Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=? for update
test

從網上找了一個鎖定模式表格:


樂觀鎖:

樂觀鎖假定當前事務操作資料時,別人不會來的,通過版本號控制。效能好。

在xxx.hbm.xml配置版本號:

 <!-- 設定主鍵 -->
 <id name="id" column="ID" type="long">
           <!-- 主鍵生成方式-->
           <generator class="increment"/>
 </id>

<version name="version" column = "VERSION"/>

必須在id配置下方,實體中建立一個version的版本號,型別可以用時間,或者疊加資料。

每次更新資料時增加,把版本號作為條件

update.....where ....and version = (你的上一個版本的值,表示在上一個版本基礎上更新資料)