亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

關(guān)于Spring中聲明式事務(wù)的使用詳解

 更新時(shí)間:2021年08月05日 09:33:25   作者:8aceSuper  
Spring中事務(wù)分為編程式事務(wù)和聲明式事務(wù),編程式事務(wù)由于需要在代碼中硬編碼,在實(shí)際項(xiàng)目開發(fā)中比較少用到,實(shí)際開發(fā)中用的比較多的就是聲明式事務(wù),這篇文章主要給大家介紹了關(guān)于Spring中聲明式事務(wù)使用的相關(guān)資料,需要的朋友可以參考下

一、前言

在Spring中,數(shù)據(jù)庫事務(wù)是通過AOP技術(shù)來提供服務(wù)的。使用原生的JDBC操作時(shí)會(huì)存在大量的try{}catch{}finally{}語句,所以會(huì)存在大量的冗余代碼,例如打開和關(guān)閉數(shù)據(jù)庫連接和數(shù)據(jù)庫事務(wù)回滾等。通過Spring的AOP之后,這些冗余的代碼就都被處理了。

二、回顧JDBC的數(shù)據(jù)庫事務(wù)

接下來我們一起回顧一下,剛?cè)腴T使用JDBC操作的時(shí)候,寫得讓人煩躁代碼片段吧。

@Service
public class JdbcTransaction {
    @Autowired
    private DataSource dataSource;

    public int insertStudent(Student student) {
        Connection connection = null;
        int result = 0;
        try {
            // 獲取數(shù)據(jù)連接
            connection = dataSource.getConnection();
            // 開始事務(wù)
            connection.setAutoCommit(false);
            // 設(shè)置隔離級(jí)別
            connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            // 執(zhí)行sql
            PreparedStatement prepareStatement = connection.prepareStatement("insert into t_student(name,gender,age) values (?,?,?)");
            prepareStatement.setString(1, student.getName());
            prepareStatement.setString(2, student.getGender());
            prepareStatement.setInt(3, student.getAge());
            result = prepareStatement.executeUpdate();
            // 提交事務(wù)
            connection.commit();
        } catch (Exception e1) {
            if (connection != null) {
                try {
                    // 事務(wù)回滾
                    connection.rollback();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            e1.printStackTrace();
        } finally {
            try {
                if (connection != null && !connection.isClosed()) {
                    // 關(guān)閉連接
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

在上述的一大串代碼中也就一下已行的業(yè)務(wù)代碼是我們最為關(guān)注的,在每個(gè)使用JDBC的業(yè)務(wù)代碼中,都經(jīng)??梢钥吹綌?shù)據(jù)庫連接的獲取和關(guān)閉以及事務(wù)的提交和回滾,大量的try...catch...finally..語句會(huì)充斥在代碼塊中。而我們也僅是想執(zhí)行簡(jiǎn)單的一條sql代碼而已。如果執(zhí)行多條sql,這些代碼顯然更加的就難以控制。

PreparedStatement prepareStatement = connection.prepareStatement("insert into t_student(name,gender,age) values (?,?,?)");
prepareStatement.setString(1, student.getName());
prepareStatement.setString(2, student.getGender());
prepareStatement.setInt(3, student.getAge());
result = prepareStatement.executeUpdate();

隨著不斷地的發(fā)展和優(yōu)化,使用像MyBatis或Hibernate這種ORM框架可以減少一些代碼,但是依舊不能減少打開和關(guān)閉數(shù)據(jù)庫連接和事務(wù)控制的代碼,但是這些我們可以通過AOP把這些公共代碼抽取出來,單獨(dú)實(shí)現(xiàn)。

三、數(shù)據(jù)庫事務(wù)隔離級(jí)別

3.1 數(shù)據(jù)庫事務(wù)的基本特征

  • Atomicity(原子性):一個(gè)事務(wù)(transaction)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會(huì)被恢復(fù)(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。
  • Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度、串聯(lián)性以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作。
  • Isolation(隔離性):數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級(jí)別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)。這也是本節(jié)要講的內(nèi)容。
  • Durability(持久性):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

這里引用一下網(wǎng)上用的很多的講述隔離性的例子。假設(shè)有一種商品有庫存100,每次都只能搶購一件商品。就會(huì)出現(xiàn)各種情況:

第一類丟失更新

時(shí)刻 事務(wù)1 事務(wù)2
T1 初始庫存100 初始庫存100
T2 扣減庫存,剩余99
T3 扣減庫存,剩余99
T4 提交事務(wù),商品庫存為99
T5 回滾事務(wù),商品庫存為100

像上面所述,對(duì)于一個(gè)事務(wù)回滾了另外一個(gè)事務(wù)提交,而引發(fā)的數(shù)據(jù)不一致的情況,被稱為第一類丟失更新。目前的數(shù)據(jù)庫已經(jīng)解決了第一類丟失更新的問題,即上述表中描述的問題。
第二類丟失更新

時(shí)刻 事務(wù)1 事務(wù)2
T1 初始庫存100 初始庫存100
T2 扣減庫存,剩余99
T3 扣減庫存,剩余99
T4 提交事務(wù),剩余庫存99
T5 提交事務(wù),剩余庫存99

在事務(wù)1中無法感知事務(wù)2的操作,所以事務(wù)1并不知道事務(wù)2修改過數(shù)據(jù),因此它認(rèn)為只是發(fā)生了一個(gè)業(yè)務(wù),所以庫存還是被事務(wù)1修改成99。T5時(shí)刻事務(wù)1的提交,導(dǎo)致事務(wù)2提交結(jié)果丟失,這樣多個(gè)事務(wù)都提交引發(fā)的數(shù)據(jù)丟失更新稱為第二類丟失更新。這是互聯(lián)網(wǎng)系統(tǒng)重點(diǎn)關(guān)注的內(nèi)容,為了克服這個(gè)問題,數(shù)據(jù)庫提出了事務(wù)之間的隔離級(jí)別的概念。

3.2 詳解數(shù)據(jù)庫隔離級(jí)別

為了壓制更新丟失,數(shù)據(jù)庫標(biāo)準(zhǔn)踢輸了四種隔離級(jí)別,在不同程度上壓制丟失更新。這四類隔離級(jí)別就是上面提到的:未提交讀,讀寫提交,可重復(fù)讀和串行化,它們會(huì)在不同程度上壓制丟失更新的情況。

3.2.1 未提交讀

未提交讀是數(shù)據(jù)庫最低的隔離級(jí)別,它允許一個(gè)事務(wù)讀取另外一個(gè)事務(wù)沒有提交的數(shù)據(jù)。未提交讀是很危險(xiǎn)的隔離級(jí)別,一般在實(shí)際開發(fā)中并沒有廣泛使用,它的最大的有點(diǎn)就是并發(fā)能力高,適合一些對(duì)數(shù)據(jù)一致性沒有要求但追求高并發(fā)的場(chǎng)景,最大的缺點(diǎn)就是會(huì)造成臟讀。
臟讀現(xiàn)象

時(shí)刻 事務(wù)1 事務(wù)2 備注
T0 商品的庫存初始化數(shù)量為100
T1 讀取庫存100
T2 扣減庫存 此時(shí)庫存為99
T3 扣減庫存 此時(shí)庫存為98,讀取到了事務(wù)1沒提交的數(shù)據(jù)
T4 提交事務(wù) 庫存保存為98
T5 回滾事務(wù) 因?yàn)榈谝活悂G失更新已經(jīng)解決,所以庫存不會(huì)回滾到100,此時(shí)庫存為98

如果數(shù)據(jù)使用了未提交讀的隔離級(jí)別,就可能出現(xiàn)上述表格中的問題。這種現(xiàn)象被稱為臟讀,事務(wù)2讀取到了事務(wù)1還沒提交的數(shù)據(jù),當(dāng)事務(wù)1回滾之后,這數(shù)據(jù)便成為了臟數(shù)據(jù)。在讀寫提交的隔離級(jí)別中就克服了臟讀的現(xiàn)象。

3.2.2 讀提交

讀寫提交隔離級(jí)別,是指一個(gè)事務(wù)只能讀取到另外一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù),不能讀取未提交的數(shù)據(jù)。

時(shí)刻 事務(wù)1 事務(wù)2 備注
T0 商品的庫存初始化數(shù)量為100
T1 讀取庫存100
T2 扣減庫存 此時(shí)庫存為99
T3 扣減庫存 此時(shí)庫存為99,讀取不了事務(wù)1沒提交的數(shù)據(jù)
T4 提交事務(wù) 庫存保存為99
T5 回滾事務(wù) 因?yàn)榈谝活悂G失更新已經(jīng)解決,所以庫存不會(huì)回滾到100,此時(shí)庫存為99

這就是讀提交,若有事務(wù)對(duì)數(shù)據(jù)進(jìn)行更新操作時(shí),讀操作事務(wù)要等待這個(gè)更新操作事務(wù)提交后才能讀取數(shù)據(jù),可以解決臟讀問題。但在這個(gè)事例中,如果事務(wù)2先不提交,事務(wù)1未提交是查詢的庫存是99,事務(wù)1提交了事務(wù)2再去查詢庫存此時(shí)庫存是98,這就出現(xiàn)了一個(gè)事務(wù)范圍內(nèi)兩個(gè)相同的查詢卻返回了不同數(shù)據(jù),這就是不可重復(fù)讀。

這是各種系統(tǒng)中最常用的一種隔離級(jí)別,也是SQL Server和Oracle的默認(rèn)隔離級(jí)別。這種隔離級(jí)別能夠有效的避免臟讀,但除非在查詢中顯示的加鎖,如:

select * from T where ID=2 lock in share mode;
select * from T where ID=2 for update;

很明顯讀提交隔離級(jí)別會(huì)引起不可重復(fù)讀現(xiàn)象,而可重復(fù)讀隔離級(jí)別就可以解決不可重復(fù)讀。

3.2.3 可重復(fù)讀

可重復(fù)讀的目標(biāo)就是克服讀提交中出現(xiàn)的不可重復(fù)讀的現(xiàn)象,因?yàn)樵谧x提交的時(shí)候,可能出現(xiàn)一些值的變化,影響當(dāng)前事務(wù)的執(zhí)行。

解決不可重復(fù)度

時(shí)刻 事務(wù)1 事務(wù)2 備注
T0 商品的庫存初始化數(shù)量為100
T1 讀取庫存100
T2 扣減庫存 此時(shí)庫存為99
T3 讀取庫存 不允許讀取,事務(wù)1還沒提交
T4 提交事務(wù) 庫存保存為99
T5 讀取庫存 此時(shí)庫存為99

幻讀

時(shí)刻 事務(wù)1 事務(wù)2 備注
T0 商品的庫存初始化數(shù)量為100
T1 讀取庫存100
T2 查詢訂單記錄 0筆訂單記錄
T3 扣庫存 庫存保存為99
T4 插入一個(gè)訂單記錄 新增1條訂單記錄
T5 提交事務(wù) 此時(shí)庫存為99,1條訂單記錄
T6 查詢訂單記錄 有1條訂單記錄,出現(xiàn)了查詢不一致,在事務(wù)2看來出現(xiàn)了和之前查詢不一致的結(jié)果

3.2.4 串行化

串行化是數(shù)據(jù)庫最高的隔離級(jí)別,它要求所有的sql按照順序執(zhí)行,這樣就可以克服上面所述的所有問題,所以能夠保證數(shù)據(jù)的一致性。但是性能也是最差的。

3.2.5 各個(gè)隔離級(jí)別的總結(jié)

隔離級(jí)別 臟讀 不可重復(fù)讀 歡度
未提交讀
讀提交 ×
可重復(fù)讀 × ×
串行化 × × ×

隔離級(jí)別臟讀不可重復(fù)讀歡度未提交讀√√√讀提交×√√可重復(fù)讀××√串行化×××
對(duì)于不同的數(shù)據(jù)庫的支持是不一樣的,Oracle只能支持讀提交和串行化,MySQL則是能夠支持上面四種。Oracle默認(rèn)的隔離級(jí)別是讀提交,MySQL則是可重復(fù)讀。

四、數(shù)據(jù)庫事務(wù)傳播行為

傳播行為是方法之間調(diào)用事務(wù)采取的策略問題。在通常情況下數(shù)據(jù)庫事務(wù)要么全部成功,要么全部失敗。在Spring中當(dāng)一個(gè)方法調(diào)用另外一個(gè)方法時(shí),可以讓事務(wù)采取不同的策略,例如新建一個(gè)事務(wù)處理或者掛起當(dāng)前事務(wù)等,這就是事務(wù)的傳播。例如下面deleteStudent()調(diào)用了findStudentById(id)檢查Student是否存在。

@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public Student findStudentById(Long id) {
        return studentRepository.getOne(id);
    }

    ...

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void deleteStudent(Long id) {
        Student student = findStudentById(id);
        if(student == null){
            // data not exists
        }
        studentRepository.deleteById(id);
    }
    
    ...
}

在Spring中支持的事務(wù)傳播行為:

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition;

public enum Propagation {

	/**
	 * 支持當(dāng)前事務(wù),如果不存在則創(chuàng)建一個(gè)新事務(wù)。 類似于同名的 EJB 事務(wù)屬性。
	 * 這是事務(wù)注釋的默認(rèn)設(shè)置。
	 */
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	/**
	 * 支持當(dāng)前事務(wù),如果不存在則以非事務(wù)方式執(zhí)行。 類似于同名的 EJB 事務(wù)屬性。
	 * 注意:對(duì)于具有事務(wù)同步的事務(wù)管理器, SUPPORTS與根本沒有事務(wù)略有不同,因?yàn)樗x了同步將應(yīng)用的事務(wù)范圍。
     * 因此,相同的資源(JDBC 連接、Hibernate 會(huì)話等)將在整個(gè)指定范圍內(nèi)共享。 請(qǐng)注意,這取決于事務(wù)管理器的實(shí)際同步配置
	 */
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	/**
	 * 支持當(dāng)前事務(wù),如果不存在則拋出異常。 類似于同名的 EJB 事務(wù)屬性。
	 */
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	
	/**
	 * 創(chuàng)建一個(gè)新事務(wù),如果存在,則暫停當(dāng)前事務(wù)。 類似于同名的 EJB 事務(wù)屬性。
	 * 注意:實(shí)際的事務(wù)暫停不會(huì)在所有事務(wù)管理器上開箱即用。 
	 * 這尤其適用于org.springframework.transaction.jta.JtaTransactionManager 
	 * 它需要javax.transaction.TransactionManager對(duì)其可用(這在標(biāo)準(zhǔn) Java EE 中是特定于服務(wù)器的)
	 */
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	/**
	 * 以非事務(wù)方式執(zhí)行,如果存在則暫停當(dāng)前事務(wù)。 類似于同名的 EJB 事務(wù)屬性。
	 * 注意:實(shí)際的事務(wù)暫停不會(huì)在所有事務(wù)管理器上開箱即用。 這尤其適用于org.springframework.transaction.jta.JtaTransactionManager 
	 * 它需要javax.transaction.TransactionManager對(duì)其可用(這在標(biāo)準(zhǔn) Java EE 中是特定于服務(wù)器的)。
	 */
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	/**
	 * 以非事務(wù)方式執(zhí)行,如果存在事務(wù)則拋出異常。 類似于同名的 EJB 事務(wù)屬性。
	 */
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	/**
	 * 如果當(dāng)前事務(wù)存在,則在嵌套事務(wù)中執(zhí)行,否則行為類似于REQUIRED 。 EJB 中沒有類似的特性。
	 * 注意:嵌套事務(wù)的實(shí)際創(chuàng)建僅適用于特定的事務(wù)管理器。 開箱即用,
	 * 這僅適用于 JDBC DataSourceTransactionManager。 一些 JTA 提供者也可能支持嵌套事務(wù)。
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	NESTED(TransactionDefinition.PROPAGATION_NESTED);


	private final int value;


	Propagation(int value) {
		this.value = value;
	}

	public int value() {
		return this.value;
	}

}

事務(wù)的傳播行為,默認(rèn)值為 Propagation.REQUIRED。可以手動(dòng)指定其他的事務(wù)傳播行為,如下:

(1)Propagation.REQUIRED

如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

(2)Propagation.SUPPORTS

如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。

(3)Propagation.MANDATORY

如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。

(4)Propagation.REQUIRES_NEW

重新創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),延緩當(dāng)前的事務(wù)。

(5)Propagation.NOT_SUPPORTED

以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。

(6)Propagation.NEVER

以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

(7)Propagation.NESTED

如果沒有,就新建一個(gè)事務(wù);如果有,就在當(dāng)前事務(wù)中嵌套其他事務(wù)。

五、Spring中的聲明式事務(wù)的使用

在Spring中使用事務(wù)很簡(jiǎn)單,只需要在方法上添加@Transactional注解即可,Spring的事務(wù)管理幫我們做了如,在使用JDBC的時(shí)候的那些繁瑣的try/catch代碼。

@Service
public class StudentServiceImpl implements IStudentService {

    ...
        
    @Transactional
    @Override
    public Student insertStudent(Student student) {
        return studentRepository.save(student);
    }

    ...
}

當(dāng)Spring上下文開始調(diào)用被@Transactional修飾的方法或者類時(shí),Spring就會(huì)產(chǎn)生AOP的功能,Spring中的事務(wù)管理是基于AOP的。當(dāng)啟動(dòng)事務(wù)時(shí),會(huì)根據(jù)事務(wù)定義器內(nèi)的配置去設(shè)置事務(wù),首先是根據(jù)傳播行為來確定事務(wù)的策略,上一節(jié)說道,Spring中默認(rèn)的傳播行為是Propagation.REQUIRED(如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù))。然后是隔離級(jí)別、超時(shí)時(shí)間、只讀內(nèi)容等內(nèi)容的設(shè)置,這些Spring中的事務(wù)管理器都有默認(rèn)的設(shè)置,開發(fā)者只需要直接使用@Transactional注解即可,如果不滿足也可以自行配置。

通過@Transactional注解的屬性配置去設(shè)置數(shù)據(jù)庫的事務(wù),在程序執(zhí)行到開發(fā)者編寫的程序事,如果發(fā)生異常,Spring數(shù)據(jù)庫事務(wù)的流程中,它會(huì)根據(jù)是否發(fā)生異常來才去不同的策略。無論是否發(fā)生異常,Spring事務(wù)管理器會(huì)釋放事務(wù)資源,這樣就可以保證數(shù)據(jù)庫連接的正??捎?。這樣就減少了

5.1 @Transactional的配置屬性

我們來看一下@Transactiona注解的源碼:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    // 通過bean name執(zhí)行事務(wù)管理器
    @AliasFor("transactionManager")
    String value() default "";
	// 同value屬性
    @AliasFor("value")
    String transactionManager() default "";
	// 指定事務(wù)傳播行為,默認(rèn)是Propagation.REQUIRED
    Propagation propagation() default Propagation.REQUIRED;
	// 指定事務(wù)的隔離級(jí)別,默認(rèn)是使用底層數(shù)據(jù)存儲(chǔ)的默認(rèn)隔離級(jí)別。如MySQL是可重復(fù)讀
    Isolation isolation() default Isolation.DEFAULT;
	// 指定超時(shí)時(shí)間,單位時(shí)間秒
    int timeout() default -1;
	// 是否是只讀事務(wù)
    boolean readOnly() default false;
	// 在發(fā)生指定的異常是回滾事務(wù),默認(rèn)是所有異常都回滾
    Class<? extends Throwable>[] rollbackFor() default {};
	// 方法在發(fā)生指定異常名稱時(shí)回滾事務(wù),默認(rèn)是所有異常都回滾
    String[] rollbackForClassName() default {};
	// 在發(fā)生指定的異常是不回滾事務(wù)
    Class<? extends Throwable>[] noRollbackFor() default {};
	// 方法在發(fā)生指定異常名稱時(shí)不回滾事務(wù)
    String[] noRollbackForClassName() default {};
}

5.2 Spring的事務(wù)管理器

Spring中的事務(wù)的打開、回滾和提交是由事務(wù)管理器來完成的。Spring中事務(wù)的頂層接口是TransactionManager這是個(gè)空接口,真正定義了方法的PlatformTransactionManager接口。當(dāng)引入了其他框架的時(shí)候還會(huì)有其他的事務(wù)管理器的類,例如HibernateTransactionManager和JpaTransactionManager就是spring-orm這依賴?yán)锩妫荢pring官方編寫的提供的。如果引入的Redisson,還會(huì)有RedissonTransactionManager。最常用的就是DataSourceTransactionManger,它

也是實(shí)現(xiàn)中用的最多的的事務(wù)管理器。

PlatformTransactionManager接口中只有三個(gè)方法,獲取事務(wù),提交事務(wù)和回滾事務(wù)。這是事務(wù)的最基本的。不同的事務(wù)管理就可以在此基礎(chǔ)上實(shí)現(xiàn)自己的功能。例如在Spring Boot中引入了spring-boot-starter-data-jpa依賴之后,就會(huì)自動(dòng)創(chuàng)建JpaTransactionManager作為事務(wù)管理器,所以一般是不需要我們自己創(chuàng)建事務(wù)管理器,除非有特定的需求。

public interface PlatformTransactionManager extends TransactionManager {
	// 獲取事務(wù)m還可以設(shè)置數(shù)據(jù)屬性
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
	// 提交事務(wù)
	void commit(TransactionStatus status) throws TransactionException;
	// 回滾事務(wù)
	void rollback(TransactionStatus status) throws TransactionException;

}

5.3 配置事務(wù)的傳播行為和隔離級(jí)別

現(xiàn)在配置測(cè)試一下BatchServiceImpl調(diào)用StudentServiceImpl的方法。

插入一個(gè)Student

@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Transactional(rollbackFor = RuntimeException.class, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW)
    @Override
    public Student insertStudent(Student student) {
        return studentRepository.save(student);
    }

}

批量插入

@Service
public class BatchServiceImpl implements IBatchService {

    @Autowired
    private IStudentService studentService;

    @Transactional(rollbackFor = RuntimeException.class, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED)
    @Override
    public void batchInsertStudent(List<Student> students) {
        for (Student student : students) {
            studentService.insertStudent(student);
        }
    }
}

batchInsertStudent方法使用Propagation.REQUIRED的傳播行為,數(shù)據(jù)庫隔離級(jí)別使用REPEATABLE_READ。調(diào)用的insertStudent方法,其傳播行為是REQUIRES_NEW,被調(diào)用時(shí)會(huì)新開一個(gè)事務(wù)。

關(guān)于@Transactional自調(diào)用,傳播行為失效的問題

如下代碼,在同一個(gè)類中相互調(diào)用的方法,會(huì)導(dǎo)致@Transactional中定義的傳播行為失效。在自調(diào)用的過程中,是類的自身的調(diào)用,而不是代理對(duì)象去調(diào)用,那么就不會(huì)產(chǎn)生AOP,這樣自調(diào)用就不會(huì)被事務(wù)管理器管理,就不能把代碼織入到約定的流程中。像一個(gè)service調(diào)用另外一個(gè)service的,這樣就是代理對(duì)象的調(diào)用,Spring才把代碼織入到AOP的流程中。

@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public Student findStudentById(Long id) {
        return studentRepository.getOne(id);
    }

    @Transactional(rollbackFor = RuntimeException.class, isolation = Isolation.REPEATABLE_READ)
    @Override
    public void deleteStudent(Long id) {
        // 自調(diào)用
        Student db = findStudentById(id);
        if (db == null) {
            throw new RuntimeException("數(shù)據(jù)不存在");
        }
        studentRepository.deleteById(id);
    }

}

解決自調(diào)用是事務(wù)傳播行為失效的問題

通過在Spring上下文中獲取IStudentService對(duì)象,此時(shí)該對(duì)象就是個(gè)代理對(duì)象,這樣通過代理對(duì)象去調(diào)用就可以出發(fā)傳播行為了。

@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    private StudentRepository studentRepository;
    
    // 注入Spring上下文對(duì)象
    @Autowired
    private ApplicationContext context;

    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public Student findStudentById(Long id) {
        return studentRepository.getOne(id);
    }

    @Transactional(rollbackFor = RuntimeException.class, isolation = Isolation.REPEATABLE_READ)
    @Override
    public void deleteStudent(Long id) {
        // 從上下文中獲取IStudentService
        IStudentService studentService = context.getBean(IStudentService.class);
        Student db = findStudentById(id);
        if (db == null) {
            throw new RuntimeException("數(shù)據(jù)不存在");
        }
        studentRepository.deleteById(id);
    }

}

六、總結(jié)

本文從原生JDBC的事務(wù)管理開始到介紹數(shù)據(jù)庫的隔離級(jí)別和Spring中的傳播行為,最后使用Spring的聲明式事務(wù),其實(shí)在正常的使用中,Spring的聲明式事務(wù)用起來很簡(jiǎn)單和簡(jiǎn)潔,這是Spring內(nèi)部幫我們做好了很多的事情。

到此這篇關(guān)于Spring中聲明式事務(wù)使用的文章就介紹到這了,更多相關(guān)Spring聲明式事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 通過反射注解批量插入數(shù)據(jù)到DB的實(shí)現(xiàn)方法

    通過反射注解批量插入數(shù)據(jù)到DB的實(shí)現(xiàn)方法

    今天小編就為大家分享一篇關(guān)于通過反射注解批量插入數(shù)據(jù)到DB的實(shí)現(xiàn)方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能

    Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能

    這篇文章介紹了Java使用easyExcel實(shí)現(xiàn)導(dǎo)入功能的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • Java通過反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

    Java通過反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

    這篇文章主要介紹了Java通過反射,動(dòng)態(tài)修改注解的某個(gè)屬性值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot 整合 Lettuce Redis的實(shí)現(xiàn)方法

    SpringBoot 整合 Lettuce Redis的實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot 整合 Lettuce Redis的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Spring中使用copyProperties方法進(jìn)行對(duì)象之間的屬性賦值詳解

    Spring中使用copyProperties方法進(jìn)行對(duì)象之間的屬性賦值詳解

    這篇文章主要介紹了Spring中使用copyProperties方法進(jìn)行對(duì)象之間的屬性賦值詳解,使用org.springframework.beans.BeanUtils.copyProperties方法進(jìn)行對(duì)象之間屬性的賦值,避免通過get、set方法一個(gè)一個(gè)屬性的賦值,需要的朋友可以參考下
    2023-12-12
  • Java中數(shù)組轉(zhuǎn)List的三種方法與對(duì)比分析

    Java中數(shù)組轉(zhuǎn)List的三種方法與對(duì)比分析

    這篇文章主要給大家介紹了關(guān)于Java中數(shù)組轉(zhuǎn)List的三種方法與對(duì)比分析的相關(guān)資料,分別介紹了最常見方式、數(shù)組轉(zhuǎn)為L(zhǎng)ist后,支持增刪改查的方式以及通過集合工具類Collections.addAll()方法,需要的朋友可以參考下
    2018-07-07
  • Springboot使用ResponseBody漢字返回問號(hào)問題

    Springboot使用ResponseBody漢字返回問號(hào)問題

    這篇文章主要介紹了Springboot使用ResponseBody漢字返回問號(hào)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java實(shí)現(xiàn)文件上傳的兩種方法(uploadify和Spring)

    Java實(shí)現(xiàn)文件上傳的兩種方法(uploadify和Spring)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)文件上傳的兩種方法,uploadify和Spring實(shí)現(xiàn)文件上傳,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 淺談Java中Spring Boot的優(yōu)勢(shì)

    淺談Java中Spring Boot的優(yōu)勢(shì)

    在本篇文章中小編給大家分析了Java中Spring Boot的優(yōu)勢(shì)以及相關(guān)知識(shí)點(diǎn)內(nèi)容,興趣的朋友們可以學(xué)習(xí)參考下。
    2018-09-09
  • 以Java?Web項(xiàng)目為例淺談前后端分離開發(fā)模式

    以Java?Web項(xiàng)目為例淺談前后端分離開發(fā)模式

    這篇文章主要介紹了以Java?Web項(xiàng)目為例淺談前后端分離開發(fā)模式,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08

最新評(píng)論