小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Spring事務(wù)管理詳解

 liang1234_ 2018-04-13

事務(wù)的基本原理

Spring事務(wù)的本質(zhì)其實就是數(shù)據(jù)庫對事務(wù)的支持,使用JDBC的事務(wù)管理機(jī)制,就是利用java.sql.Connection對象完成對事務(wù)的提交,那在沒有Spring幫我們管理事務(wù)之前,我們要怎么做。

Connection conn = DriverManager.getConnection(); try { conn.setAutoCommit(false); //將自動提交設(shè)置為false 執(zhí)行CRUD操作 conn.commit(); //當(dāng)兩個操作成功后手動提交 } catch (Exception e) { conn.rollback(); //一旦其中一個操作出錯都將回滾,所有操作都不成功 e.printStackTrace(); } finally { conn.colse(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

事務(wù)是一系列的動作,一旦其中有一個動作出現(xiàn)錯誤,必須全部回滾,系統(tǒng)將事務(wù)中對數(shù)據(jù)庫的所有已完成的操作全部撤消,滾回到事務(wù)開始的狀態(tài),避免出現(xiàn)由于數(shù)據(jù)不一致而導(dǎo)致的接下來一系列的錯誤。事務(wù)的出現(xiàn)是為了確保數(shù)據(jù)的完整性和一致性,在目前企業(yè)級應(yīng)用開發(fā)中,事務(wù)管理是必不可少的。

與事務(wù)相關(guān)的理論知識

眾所周知,事務(wù)有四大特性(ACID)

1.原子性(Atomicity)事務(wù)是一個原子操作,由一系列動作組成。事務(wù)的原子性確保動作要么全部完成,要么完全不起作用。

2.一致性(Consistency)事務(wù)在完成時,必須是所有的數(shù)據(jù)都保持一致狀態(tài)。

3.隔離性(Isolation)并發(fā)事務(wù)執(zhí)行之間無影響,在一個事務(wù)內(nèi)部的操作對其他事務(wù)是不產(chǎn)生影響,這需要事務(wù)隔離級別來指定隔離性。

4.持久性(Durability)一旦事務(wù)完成,數(shù)據(jù)庫的改變必須是持久化的。

在企業(yè)級應(yīng)用中,多用戶訪問數(shù)據(jù)庫是常見的場景,這就是所謂的事務(wù)的并發(fā)。事務(wù)并發(fā)所可能存在的問題:
1.臟讀:一個事務(wù)讀到另一個事務(wù)未提交的更新數(shù)據(jù)。
2.不可重復(fù)讀:一個事務(wù)兩次讀同一行數(shù)據(jù),可是這兩次讀到的數(shù)據(jù)不一樣。
3.幻讀:一個事務(wù)執(zhí)行兩次查詢,但第二次查詢比第一次查詢多出了一些數(shù)據(jù)行。
4.丟失更新:撤消一個事務(wù)時,把其它事務(wù)已提交的更新的數(shù)據(jù)覆蓋了。

我們可以在java.sql.Connection中看到JDBC定義了五種事務(wù)隔離級別來解決這些并發(fā)導(dǎo)致的問題:

/** * A constant indicating that transactions are not supported. */ int TRANSACTION_NONE = 0; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads can occur. * This level allows a row changed by one transaction to be read * by another transaction before any changes in that row have been * committed (a 'dirty read'). If any of the changes are rolled back, * the second transaction will have retrieved an invalid row. */ int TRANSACTION_READ_UNCOMMITTED = 1; /** * A constant indicating that * dirty reads are prevented; non-repeatable reads and phantom * reads can occur. This level only prohibits a transaction * from reading a row with uncommitted changes in it. */ int TRANSACTION_READ_COMMITTED = 2; /** * A constant indicating that * dirty reads and non-repeatable reads are prevented; phantom * reads can occur. This level prohibits a transaction from * reading a row with uncommitted changes in it, and it also * prohibits the situation where one transaction reads a row, * a second transaction alters the row, and the first transaction * rereads the row, getting different values the second time * (a 'non-repeatable read'). */ int TRANSACTION_REPEATABLE_READ = 4; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads are prevented. * This level includes the prohibitions in * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the * situation where one transaction reads all rows that satisfy * a <code>WHERE</code> condition, a second transaction inserts a row that * satisfies that <code>WHERE</code> condition, and the first transaction * rereads for the same condition, retrieving the additional * 'phantom' row in the second read. */ int TRANSACTION_SERIALIZABLE = 8;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

翻譯過來這幾個常量就是
TRANSACTION_NONE JDBC 驅(qū)動不支持事務(wù)
TRANSACTION_READ_UNCOMMITTED 允許臟讀、不可重復(fù)讀和幻讀。
TRANSACTION_READ_COMMITTED 禁止臟讀,但允許不可重復(fù)讀和幻讀。
TRANSACTION_REPEATABLE_READ 禁止臟讀和不可重復(fù)讀,單運(yùn)行幻讀。
TRANSACTION_SERIALIZABLE 禁止臟讀、不可重復(fù)讀和幻讀。

隔離級別越高,意味著數(shù)據(jù)庫事務(wù)并發(fā)執(zhí)行性能越差,能處理的操作就越少。你可以通過conn.setTransactionLevel去設(shè)置你需要的隔離級別。
JDBC規(guī)范雖然定義了事務(wù)的以上支持行為,但是各個JDBC驅(qū)動,數(shù)據(jù)庫廠商對事務(wù)的支持程度可能各不相同。
出于性能的考慮我們一般設(shè)置TRANSACTION_READ_COMMITTED就差不多了,剩下的通過使用數(shù)據(jù)庫的鎖來幫我們處理別的,關(guān)于數(shù)據(jù)庫的鎖這個之后再說。

了解了基本的JDBC事務(wù),那有了Spring,在事務(wù)管理上會有什么新的改變呢?
有了Spring,我們再也無需要去處理獲得連接、關(guān)閉連接、事務(wù)提交和回滾等這些操作,使得我們把更多的精力放在處理業(yè)務(wù)上。事實上Spring并不直接管理事務(wù),而是提供了多種事務(wù)管理器。他們將事務(wù)管理的職責(zé)委托給Hibernate或者JTA等持久化機(jī)制所提供的相關(guān)平臺框架的事務(wù)來實現(xiàn)。

Spring事務(wù)管理

Spring事務(wù)管理的核心接口是PlatformTransactionManager
這里寫圖片描述
事務(wù)管理器接口通過getTransaction(TransactionDefinition definition)方法根據(jù)指定的傳播行為返回當(dāng)前活動的事務(wù)或創(chuàng)建一個新的事務(wù),這個方法里面的參數(shù)是TransactionDefinition類,這個類就定義了一些基本的事務(wù)屬性。
在TransactionDefinition接口中定義了它自己的傳播行為和隔離級別
這里寫圖片描述
除去常量,主要的方法有:

int getIsolationLevel();// 返回事務(wù)的隔離級別 String getName();// 返回事務(wù)的名稱 int getPropagationBehavior();// 返回事務(wù)的傳播行為 int getTimeout(); // 返回事務(wù)必須在多少秒內(nèi)完成 boolean isReadOnly(); // 事務(wù)是否只讀,事務(wù)管理器能夠根據(jù)這個返回值進(jìn)行優(yōu)化,確保事務(wù)是只讀的
  • 1
  • 2
  • 3
  • 4
  • 5

Spring事務(wù)的傳播屬性

由上圖可知,Spring定義了7個以PROPAGATION_開頭的常量表示它的傳播屬性。

名稱 解釋
PROPAGATION_REQUIRED 0 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇,也是Spring默認(rèn)的事務(wù)的傳播。
PROPAGATION_SUPPORTS 1 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
PROPAGATION_MANDATORY 2 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
PROPAGATION_REQUIRES_NEW 3 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
PROPAGATION_NOT_SUPPORTED 4 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
PROPAGATION_NEVER 5 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
PROPAGATION_NESTED 6 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

Spring事務(wù)的隔離級別

名稱 解釋
ISOLATION_DEFAULT -1 這是一個PlatfromTransactionManager默認(rèn)的隔離級別,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別。另外四個與JDBC的隔離級別相對應(yīng)
ISOLATION_READ_UNCOMMITTED 1 這是事務(wù)最低的隔離級別,它充許另外一個事務(wù)可以看到這個事務(wù)未提交的數(shù)據(jù)。這種隔離級別會產(chǎn)生臟讀,不可重復(fù)讀和幻讀。
ISOLATION_READ_COMMITTED 2 保證一個事務(wù)修改的數(shù)據(jù)提交后才能被另外一個事務(wù)讀取。另外一個事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)。
ISOLATION_REPEATABLE_READ 4 這種事務(wù)隔離級別可以防止臟讀,不可重復(fù)讀。但是可能出現(xiàn)幻讀。
ISOLATION_SERIALIZABLE 8 這是花費(fèi)最高代價但是最可靠的事務(wù)隔離級別。事務(wù)被處理為順序執(zhí)行。除了防止臟讀,不可重復(fù)讀外,還避免了幻讀。

調(diào)用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現(xiàn)
TransactionStatus接口
這里寫圖片描述
主要的方法有:

void flush();//如果適用的話,這個方法用于刷新底層會話中的修改到數(shù)據(jù)庫,例如,所有受影響的Hibernate/JPA會話。 boolean hasSavepoint(); // 是否有恢復(fù)點(diǎn) boolean isCompleted();// 是否已完成 boolean isNewTransaction(); // 是否是新的事務(wù) boolean isRollbackOnly(); // 是否為只回滾 void setRollbackOnly(); // 設(shè)置為只回滾
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看出返回的結(jié)果是一些事務(wù)的狀態(tài),可用來檢索事務(wù)的狀態(tài)信息。

配置事務(wù)管理器

介紹完Spring事務(wù)的管理的流程大概是怎么走的。接下來可以動手試試Spring是如何配置事務(wù)管理器的
例如我在spring-mybatis中配置的:

<!-- 配置事務(wù)管理器 --> <bean id='transactionManager' class='org.springframework.jdbc.datasource.DataSourceTransactionManager'> <property name='dataSource' ref='dataSource' /> </bean>
  • 1
  • 2
  • 3
  • 4

這配置不是唯一的,可以根據(jù)自己項目選擇的數(shù)據(jù)訪問框架靈活配置事務(wù)管理器

配置了事務(wù)管理器后,事務(wù)當(dāng)然還是得我們自己去操作,Spring提供了兩種事務(wù)管理的方式:編程式事務(wù)管理和聲明式事務(wù)管理,讓我們分別看看它們是怎么做的吧。

編程式事務(wù)管理

編程式事務(wù)管理我們可以通過PlatformTransactionManager實現(xiàn)來進(jìn)行事務(wù)管理,同樣的Spring也為我們提供了模板類TransactionTemplate進(jìn)行事務(wù)管理,下面主要介紹模板類,我們需要在配置文件中配置

<!--配置事務(wù)管理的模板--> <bean id='transactionTemplate' class='org.springframework.transaction.support.TransactionTemplate'> <property name='transactionManager' ref='transactionManager'></property> <!--定義事務(wù)隔離級別,-1表示使用數(shù)據(jù)庫默認(rèn)級別--> <property name='isolationLevelName' value='ISOLATION_DEFAULT'></property> <property name='propagationBehaviorName' value='PROPAGATION_REQUIRED'></property> </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

TransactionTemplate幫我們封裝了許多代碼,節(jié)省了我們的工作。下面我們寫個單元測試來測測。
為了測試事務(wù)回滾,專門建了一張tbl_accont表,用于模擬存錢的一個場景。service層主要代碼如下,后面會給出全部代碼的github地址,有需要的朋友請移步查看。
BaseSeviceImpl

//方便測試直接寫的sql @Override public void insert(String sql, boolean flag) throws Exception { dao.insertSql(sql); // 如果flag 為 true ,拋出異常 if (flag){ throw new Exception('has exception!!!'); } } //獲取總金額 @Override public Integer sum(){ return dao.sum(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

dao對應(yīng)的sum方法

<select id='sum' resultType='java.lang.Integer'> SELECT SUM(money) FROM tbl_account; </select>
  • 1
  • 2
  • 3

下面看看測試代碼

package com.gray; import com.gray.service.BaseSevice; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Resource; /** * Created by gray on 2017/4/8. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {'classpath:spring-test.xml'}) public class TransactionTest{ @Resource private TransactionTemplate transactionTemplate; @Autowired private BaseSevice baseSevice; @Test public void transTest() { System.out.println('before transaction'); Integer sum1 = baseSevice.sum(); System.out.println('before transaction sum: ' sum1); System.out.println('transaction....'); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try{ baseSevice.insert('INSERT INTO tbl_account VALUES (100);',false); baseSevice.insert('INSERT INTO tbl_account VALUES (100);',false); } catch (Exception e){ //對于拋出Exception類型的異常且需要回滾時,需要捕獲異常并通過調(diào)用status對象的setRollbackOnly()方法告知事務(wù)管理器當(dāng)前事務(wù)需要回滾 status.setRollbackOnly(); e.printStackTrace(); } } }); System.out.println('after transaction'); Integer sum2 = baseSevice.sum(); System.out.println('after transaction sum: ' sum2); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

當(dāng)baseSevice.insert的第二個參數(shù)為false時,我們假設(shè)插入數(shù)據(jù)沒有出現(xiàn)任何問題,測試結(jié)果如圖所示:
這里寫圖片描述
當(dāng)?shù)诙€參數(shù)為true時,insert會拋出一個異常,這是事務(wù)就應(yīng)該回滾,數(shù)據(jù)前后不應(yīng)該有變化,如圖所示:
這里寫圖片描述

聲明式事務(wù)管理

聲明式事務(wù)管理有兩種常用的方式,一種是基于tx和aop命名空間的xml配置文件,一種是基于@Transactional注解,隨著Spring和Java的版本越來越高,大家越趨向于使用注解的方式,下面我們兩個都說。
1.基于tx和aop命名空間的xml配置文件
配置文件

<tx:advice id='advice' transaction-manager='transactionManager'> <tx:attributes> <tx:method name='insert' propagation='REQUIRED' read-only='false' rollback-for='Exception'/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id='pointCut' expression='execution (* com.gray.service.*.*(..))'/> <aop:advisor advice-ref='advice' pointcut-ref='pointCut'/> </aop:config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

測試代碼

@Test public void transTest() { System.out.println('before transaction'); Integer sum1 = baseSevice.sum(); System.out.println('before transaction sum: ' sum1); System.out.println('transaction....'); try{ baseSevice.insert('INSERT INTO tbl_account VALUES (100);',true); } catch (Exception e){ e.printStackTrace(); } System.out.println('after transaction'); Integer sum2 = baseSevice.sum(); System.out.println('after transaction sum: ' sum2); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

事務(wù)正常執(zhí)行結(jié)果截圖
這里寫圖片描述
事務(wù)出現(xiàn)異常結(jié)果截圖
這里寫圖片描述
2.基于@Transactional注解
這種方式最簡單,也是最為常用的,只需要在配置文件中開啟對注解事務(wù)管理的支持。

<!-- 聲明式事務(wù)管理 配置事物的注解方式注入--> <tx:annotation-driven transaction-manager='transactionManager'/>
  • 1
  • 2

然后在需要事務(wù)管理的地方加上@Transactional注解,如:

@Transactional(rollbackFor=Exception.class) public void insert(String sql, boolean flag) throws Exception { dao.insertSql(sql); // 如果flag 為 true ,拋出異常 if (flag){ throw new Exception('has exception!!!'); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

rollbackFor屬性指定出現(xiàn)Exception異常的時候回滾,遇到檢查性的異常需要回滾,默認(rèn)情況下非檢查性異常,包括error也會自動回滾。
測試代碼和上面那個一樣
事務(wù)正常執(zhí)行結(jié)果截圖
這里寫圖片描述
事務(wù)出現(xiàn)異常結(jié)果截圖
這里寫圖片描述
以上就是對Spring事務(wù)進(jìn)行了詳細(xì)的分析和代碼示例。
最后是測試代碼Spring事務(wù)測試

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多