1. 程式人生 > >Spring-jdbc:事務管理器的使用

Spring-jdbc:事務管理器的使用

事務的概念

首先要明確一下事務的概念:

事務是一系列的動作,它們被當做一個單獨的工作單元。這些動作要麼全部完成要麼全部不起作用。

事務管理是企業級應用程式開發中必不可少的技術, 用來確保資料的完整性和一致性。

事務有四個關鍵屬性:

1、原子性(atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保這些動作要麼全部完成要麼一個也不起作用。

2、一致性(consistency):一旦事務中所有動作完成,事務就會被提交,資料和資源就處於一種滿足業務規則的一致性狀態中。事務執行的結果必須是使資料庫從一個一致性狀態變到另一個一致性狀態。因此當資料庫只包含成功事務提交的結果時,就說資料庫處於一致性狀態。如果資料庫系統 執行中發生故障,有些事務尚未完成就被迫中斷,這些未完成事務對資料庫所做的修改有一部分已寫入物理資料庫,這時資料庫就處於一種不正確的狀態,或者說是 不一致的狀態。

3、隔離性(isolation):一個事務的執行不能其它事務干擾。即一個事務內部的操作及使用的資料對其它併發事務是隔離的,併發執行的各個事務之間不能互相干擾。

4、永續性(durability):也稱永久性,指一個事務一旦提交,它對資料庫中的資料的改變就應該是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。

任何一個事務都必須滿足以上四個特性。

Spring 在不同的事務管理 API 之上定義了一個抽象層. 而應用程式開發人員不必瞭解底層的事務管理 API, 就可以使用 Spring 的事務管理機制.
Spring 既支援程式設計式事務管理, 也支援宣告式的事務管理.
程式設計式事務管理: 將事務管理程式碼嵌入到業務方法中來控制事務的提交和回滾. 在程式設計式管理事務時, 必須在每個事務操作中包含額外的事務管理程式碼.
宣告式事務管理: 大多數情況下比程式設計式事務管理更好用. 它將事務管理程式碼從業務方法中分離出來, 以宣告的方式來實現事務管理. 事務管理作為一種橫切關注點, 可以通過 AOP 方法模組化. Spring 通過 Spring AOP 框架支援宣告式事務管理.

Spring中的事務管理

簡單的來說:

對於純JDBC操作資料庫,想要用到事務,可以按照以下步驟進行:

獲取連線 Connection con = DriverManager.getConnection()
開啟事務con.setAutoCommit(true/false);
執行CRUD
提交事務/回滾事務 con.commit() / con.rollback();
關閉連線 conn.close();

使用Spring的事務管理功能後,我們可以不再寫步驟 2 和 4 的程式碼,而是由Spirng 自動完成。那麼Spring是如何在我們書寫的 CRUD 之前和之後開啟事務和關閉事務的呢?解決這個問題,也就可以從整體上理解Spring的事務管理實現原理了。下面簡單地介紹下,註解方式為例子

配置檔案開啟註解驅動,在相關的類和方法上通過註解@Transactional標識。
spring 在啟動的時候會去解析生成相關的bean,這時候會檢視擁有相關注解的類和方法,並且為這些類和方法生成代理,並根據@Transaction的相關引數進行相關配置注入,這樣就在代理中為我們把相關的事務處理掉了(開啟正常提交事務,異常回滾事務)。
真正的資料庫層的事務提交和回滾是通過binlog或者redo log實現的。

事務的傳播屬性

另一個需要注意的問題是,如何處理事務和事務之間的關係。一個典型的例子是當事務方法被另一個事務方法呼叫時, 必須指定事務應該如何傳播(通過propagation屬性定義)。比如: 方法可能繼續在現有事務中執行, 也可能開啟一個新事務, 並在自己的事務中執行.事務的傳播行為可以由傳播屬性指定. Spring 定義了 7 種類傳播行為.

前兩種是使用最多的屬性值。

對前兩種屬性的說明:

REQUIRED 傳播行為:

下圖是一個purchase() 事務方法被另一個事務方法 checkout() 呼叫時, 它預設會在現有的事務內執行. 這個預設的傳播行為就是 REQUIRED. 因此在 checkout() 方法的開始和終止邊界內只有一個事務. 這個事務只在 checkout() 方法結束的時候被提交。也就意味著如果有一個purchase() 沒有成功執行,那麼另一個做的更改也不會被提交。


REQUIRES_NEW 傳播行為:

另一種常見的傳播行為是 REQUIRES_NEW. 它表示該方法必須啟動一個新事務, 並在自己的事務內執行. 如果有事務在執行, 就應該先掛起它.



執行順序為:Tx1開始->Tx1掛起->Tx2開始->Tx2結束->Tx1繼續->Tx1掛起->Tx3開始->Tx3結束->Tx1繼續->Tx1結束

注意:這些屬性指定是針對被呼叫的事務方法的。

接下來我們通過分析一些巢狀事務的場景,來深入理解spring事務傳播的機制。

假設外層事務 Service A 的 Method A() 呼叫 內層Service B 的 Method B()

PROPAGATION_REQUIRED(spring 預設)


如果ServiceB.methodB() 的事務級別定義為 PROPAGATION_REQUIRED,那麼執行 ServiceA.methodA() 的時候spring已經起了事務,這時呼叫 ServiceB.methodB(),ServiceB.methodB() 看到自己已經執行在 ServiceA.methodA() 的事務內部,就不再起新的事務。

假如 ServiceB.methodB() 執行的時候發現自己沒有在事務中,他就會為自己分配一個事務。

這樣,在 ServiceA.methodA() 或者在 ServiceB.methodB() 內的任何地方出現異常,事務都會被回滾。

PROPAGATION_REQUIRES_NEW

比如我們設計 ServiceA.methodA() 的事務級別為 PROPAGATION_REQUIRED,ServiceB.methodB() 的事務級別為 PROPAGATION_REQUIRES_NEW。

那麼當執行到 ServiceB.methodB() 的時候,ServiceA.methodA() 所在的事務就會掛起,ServiceB.methodB() 會起一個新的事務,等待 ServiceB.methodB() 的事務完成以後,它才繼續執行。

他與 PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。因為 ServiceB.methodB() 是新起一個事務,那麼就是存在兩個不同的事務。如果 ServiceB.methodB() 已經提交,那麼 ServiceA.methodA() 失敗回滾,ServiceB.methodB() 是不會回滾的。如果 ServiceB.methodB() 失敗回滾,如果他丟擲的異常被 ServiceA.methodA() 捕獲,ServiceA.methodA() 事務仍然可能提交(主要看B丟擲的異常是不是A會回滾的異常)。

PROPAGATION_SUPPORTS


假設ServiceB.methodB() 的事務級別為 PROPAGATION_SUPPORTS,那麼當執行到ServiceB.methodB()時,如果發現ServiceA.methodA()已經開啟了一個事務,則加入當前的事務,如果發現ServiceA.methodA()沒有開啟事務,則自己也不開啟事務。這種時候,內部方法的事務性完全依賴於最外層的事務。

PROPAGATION_NESTED

現在的情況就變得比較複雜了, ServiceB.methodB() 的事務屬性被配置為 PROPAGATION_NESTED, 此時兩者之間又將如何協作呢?

ServiceB#methodB 如果 rollback, 那麼內部事務(即 ServiceB#methodB) 將回滾到它執行前的 SavePoint 而外部事務(即 ServiceA#methodA) 可以有以下兩種處理方式:

a、捕獲異常,執行異常分支邏輯

void methodA() {    
        try {    
            ServiceB.methodB();    
        } catch (SomeException) {    
            // 執行其他業務, 如 ServiceC.methodC();    
        }    
    }

這種方式也是巢狀事務最有價值的地方, 它起到了分支執行的效果, 如果 ServiceB.methodB 失敗, 那麼執行 ServiceC.methodC(), 而 ServiceB.methodB 已經回滾到它執行之前的 SavePoint, 所以不會產生髒資料(相當於此方法從未執行過), 這種特性可以用在某些特殊的業務中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點。

b、 外部事務回滾/提交 程式碼不做任何修改, 那麼如果內部事務(ServiceB#methodB) rollback, 那麼首先 ServiceB.methodB 回滾到它執行之前的 SavePoint(在任何情況下都會如此), 外部事務(即 ServiceA#methodA) 將根據具體的配置決定自己是 commit 還是 rollback

資料庫的隔離級別

當同一個應用程式或者不同應用程式中的多個事務在同一個資料集上併發執行時, 可能會出現許多意外的問題
併發事務所導致的問題可以分為下面三種類型:
髒讀:一事務對資料進行了增刪改,但未提交,另一事務可以讀取到未提交的資料。如果第一個事務這時候回滾了,那麼第二個事務就讀到了髒資料。

不可重複讀:一個事務中發生了兩次讀操作,第一次讀操作和第二次操作之間,另外一個事務對資料進行了修改,這時候兩次讀取的資料是不一致的。

幻讀:第一個事務對一定範圍的資料進行批量修改,第二個事務在這個範圍增加一條資料,這時候第一個事務就會丟失對新增資料的修改。
從理論上來說, 事務應該彼此完全隔離, 以避免併發事務所導致的問題. 然而, 那樣會對效能產生極大的影響, 因為事務必須按順序執行。在實際開發中, 為了提升效能, 事務會以較低的隔離級別執行。事務的隔離級別可以通過隔離事務屬性指定(通過isolation屬性指定)。
下面是描述:
事務的隔離級別要得到底層資料庫引擎的支援, 而不是應用程式或者框架的支援.
Oracle 支援的 2 種事務隔離級別:READ_COMMITED , SERIALIZABLE
Mysql 支援 4 中事務隔離級別.
隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。
大多數的資料庫預設隔離級別為 Read Commited,比如 SqlServer、Oracle
少數資料庫預設隔離級別為:Repeatable Read 比如: MySQL InnoDB

舉個栗子

下面以一個銀行轉賬的例子說明在Spring中如何通過註解的方式使用事務管理器:

檔案佈局如下:


要使用Spring的事務管理器首先需要在xml檔案中進行配置

2、配置事務管理器:

	<!-- 配置事務管理器 -->
	<bean id = "transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name = "dataSource" ref="dataSource"></property>
	</bean>

3、啟用事務註解(需要啟用tx名稱空間)
	<!-- 啟用事務註解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

資料庫很簡單:


每人的賬戶上都有2000餘額。

定義資料庫操作介面和實現類:

package spring.tx;

public interface BankAccountDAO {
	/**
	 * 用於更新賬戶餘額:加操作
	 * @param userId 待更新賬戶的的使用者ID
	 * @param amount 增加數額
	 */
	public void updateUserAccountAdd(int userId, double amount);
	
	/**
	 * 用於減少賬戶餘額
	 * @param userId
	 * @param amount
	 */
	public void updateUserAccountDec(int userId, double amount);
}

實現類:
package spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("bankAccountDAO")
public class BankAccountDAOImp implements BankAccountDAO{
	
	@Autowired
	private JdbcTemplate jdbcTemplate = null;

	@Override
	public void updateUserAccountAdd(int userId, double amount) {
		//目標賬戶餘額加上轉賬數額
		String sql3 = "UPDATE bankaccount SET balance = balance + ? WHERE user_id = ?";
		jdbcTemplate.update(sql3, new Object[]{amount,userId});
	}

	@Override
	public void updateUserAccountDec(int userId, double amount) {
		
		//查詢餘額是否足夠,如果餘額不足則丟擲異常
		String sql = "SELECT balance FROM bankaccount WHERE user_id = ? ";
		int  balance = jdbcTemplate.queryForObject(sql, Integer.class,userId );
		if (balance<amount)
			throw new UserAccountException("餘額不足!");
		
		//原賬戶餘額減去轉賬數額
		String sql2 = "UPDATE bankaccount SET balance = balance - ? WHERE user_id = ?";
		jdbcTemplate.update(sql2, new Object[]{amount, userId});
		
	}	
}

可以看到這裡我自定義了一個異常:userAccountException
package spring.tx;


public class UserAccountException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public String getMessage() {
		// TODO Auto-generated method stub
		return super.getMessage();
	}

	public UserAccountException() {
		super();
		// TODO Auto-generated constructor stub
	}

	public UserAccountException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}
}

下面是業務邏輯的介面和實現類:
package spring.tx;

public interface BankAccountService {
	/**
	 * 
	 * @param originalAccount 轉賬原賬戶
	 * @param dstAccount 轉賬目的賬戶
	 * @param amount 轉賬的數額
	 */
	public void accountTransfer(int originalAccount, int dstAccount,double amount);
}
package spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("bankAccountService")
public class BankAccountServiceImp implements BankAccountService{
	@Autowired
	private BankAccountDAO bankAccountDao;

	//新增事務註解
	//1.使用 propagation 指定事務的傳播行為, 即當前的事務方法被另外一個事務方法呼叫時
	//如何使用事務, 預設取值為 REQUIRED, 即使用呼叫方法的事務
	//REQUIRES_NEW: 事務自己的事務, 呼叫的事務方法的事務被掛起. 
	//2.使用 isolation 指定事務的隔離級別, 最常用的取值為 READ_COMMITTED
	//3.預設情況下 Spring 的宣告式事務對所有的執行時異常進行回滾. 也可以通過對應的
	//屬性進行設定. 通常情況下去預設值即可. 
	//4.使用 readOnly 指定事務是否為只讀. 表示這個事務只讀取資料但不更新資料, 
	//這樣可以幫助資料庫引擎優化事務. 若真的只是一個只讀取資料庫值的方法, 應設定 readOnly=true
	//5.使用 timeout 指定強制回滾之前事務可以佔用的時間.  
	@Transactional(propagation=Propagation.REQUIRED,
			isolation=Isolation.READ_COMMITTED,
			readOnly=false,
			timeout=3
			)
	@Override
	public void accountTransfer(int originalAccount, int dstAccount,
			double amount) {		
		bankAccountDao.updateUserAccountAdd(dstAccount, amount);
		bankAccountDao.updateUserAccountDec(originalAccount, amount);
	}

}

注意timeOut屬性的時間單位為 秒,因為減錢操作會丟擲異常,這裡沒有處理,為了測試事務功能就把加錢操作放在前面了。


測試一下:
package spring.tx;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TransanctionTest {
	
	private BankAccountService bankAccountService = null;
	private ApplicationContext ctx = null;
	{
		ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
		bankAccountService = (BankAccountService) ctx.getBean("bankAccountService");
	}
	@Test
	public void testTransanction() {
		bankAccountService.accountTransfer(1, 2, 20000);
	}

}
這裡賬戶1向賬戶2轉賬20000,很顯然賬戶1沒有這麼多錢,所以賬戶1的減錢操作和賬戶2的加錢操作都不能被執行並丟擲異常:


資料庫中的金錢數額也沒有發生變化。

相關推薦

Spring-jdbc事務管理的使用

事務的概念 首先要明確一下事務的概念: 事務是一系列的動作,它們被當做一個單獨的工作單元。這些動作要麼全部完成要麼全部不起作用。 事務管理是企業級應用程式開發中必不可少的技術, 用來確保資料的完整性和一致性。 事務有四個關鍵屬性: 1、原子性(atomicity):事務是一

Java進階學習第二十四天(Spring框架事務管理Spring與Hibernate整合)

一、事務控制 1、引入 使用者訪問 > Action > Service > Dao 如何保證: 在service中呼叫2次dao,其中一個dao執行失敗,整個操作要回滾 2、事務控制概述 ① 程式設計式事務控制:自己手動控制事務 Jdbc程式

spring整合hibernate事務管理配置

連結歸納的很詳細。 首先在/WEB-INF/applicationContext.xml新增以下內容:<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.

spring hibernate 整合spring使用容器JTA事務管理

說明: 1.EJB的事務是方法級別的隔離,而spring的攔截事務是類級別的,所以spring管理的jta事務在同一個類的不同方法設定不同的事務傳播策略是無效的。 2.使用了spring的事務,就不能顯示地使用hibernate的開啟事務,提交事務等,spring通過攔截器

Spring事務管理JDBC的實現

Spring事務管理的實現有許多細節,如果對整個介面框架有個大體瞭解會非常有利於我們理解事務,下面通過Spring的事務介面來了解Spring實現事務的具體策略。  Spring事務管理涉及的介面的聯絡如下: Spring宣告式事務管理器類:          

Spring整合mybatis時使用事務管理

lang pan lan -s error org 版本 batis java 異常報告:java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTi

Spring 事務——事務管理

事務管理器 Spring只是個容器,因此它並不做任何事務的具體實現。他只是提供了事務管理的介面PlatformTransactionManager,具體內容由就由各個事務管理器來實現。 Spring提供了許多內建事務管理器實現: DataSourceTransactionMan

第十二講12,spring宣告式事務管理-註解式

1,複製專案spring404 ,改名spring404-3。修改BankServiceImpl類,添加註解,package com.cruise.service.impl;import org.springframework.transaction.annotation.Tra

第十一講11.spring宣告式事務管理-xml方式

1,複製專案spring404 ,改名spring404-2,修改BankServiceImpl類,刪除宣告式事務的程式碼。宣告式事務管理的方式缺點是,事務程式碼嚴重嵌入邏輯程式碼中 package com.cruise.service.impl; import org.springframewor

spring配置多個事務管理

<tx:annotation-driven/> <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <prope

8.spring事務管理(上):Spring的資料庫程式設計、程式設計式事務管理

Spring的資料庫程式設計  Spring框架提供了JDBC模板模式------>JdbcTemplate 簡化了開發,在開發中並不經常是使用 實際開發更多使用的是Hibernate和MyBatis 1).Spring JDBCp配置 如果使用Spring JDBC操作資料庫,要有

9.spring事務管理(下):宣告式事務管理

宣告式事務管理  sprin的宣告式事務是管理AOP技術實現的事務管理,其本質是是對方法前後進行攔截,然後 在目標方法開始之前建立或者加入一個事務,在執行完成目標方法之後根據執行情況提交或者回滾事務。   宣告式事務管理優點:不需要通過程式設計的方式管理事務,因而不需要在業務邏輯程

spring宣告式事務管理基於註解的方式

1)在spring.xml中配置事務管理器DataSourceTransactionManager,<bean id="txManager" class="org.springframework.

spring事務管理的配置

DataSourceTransactionManager:事務管理器 對JDBC(Java Data Base Connectivity,java資料庫連線)進行事務管理,在spring中是對JdbcTemplate(用來進行對資料庫的增刪改查的操作,詳細介紹請見Sprin

spring配置事務管理,事務配置

<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasou

Spring技術內幕——事務的建立,掛起,迴歸,提交(事務攔截和抽象事務管理

在涉及單個數據庫區域性事務的事務處理中,事務的最終實現和資料庫的支援是緊密相關的。對區域性資料庫事務來說,一個事務處理的操作單元往往對應著一系列的資料庫操作。 Spring事務處理主要分以下三個主要的過程: (1)讀取和處理在Spring IoC容器中配置的事務處理屬性,並

Spring整合hibernate4事務管理

Spring和Hibernate整合後,通過Hibernate API進行資料庫操作時發現每次都要opensession,close,beginTransaction,commit,這些都是重複的工作,我們可以把事務管理部分交給spring框架完成。 配置事務(xml方

【轉】關於TransactionScope出錯“與基礎事務管理的通訊失敗”的解決方法

異常資訊: System.Transactions.TransactionManagerCommunicationException: 與基礎事務管理器的通訊失敗。 ---> System.Runtime.InteropServices.COMException: 由

【面試】足夠“忽悠”面試官的『Spring事務管理』原始碼閱讀梳理(建議珍藏)

PS:文章內容涉及原始碼,請耐心閱讀。     理論實踐,相輔相成 偉大領袖毛主席告訴我們實踐出真知。這是無比正確的。但是也會很辛苦。就像淘金一樣,從大量沙子中淘出金子一定是一個無比艱辛的過程。但如果真能淘出來,也一定是像金子一樣寶貴的東西。他老人家還說過,當真知上升為理論的時候

Spring 框架基礎(05)事務管理機制,和實現方式

本文原始碼:GitHub·點這裡 || GitEE·點這裡 一、Spring事務管理 1、基礎描述 Spring事務管理的本質就是封裝了資料庫對事務支援的操作,使用JDBC的事務管理機制,就是利用java.sql.Connection物件完成對事務的提交和回滾。 Connection conn = Drive