Spring事务和MySQL事务详解面试
文章目錄
- 數據庫事務
- 事務是什么
- 事務的四大特性
- MySQL事務隔離級別
- 查看MySQL當前事務隔離級別
- MySQL默認操作模式為自動提交模式
- JDBC處理事務
- Spring事務
- Spring的事務傳播
- PROPAGATION_REQUIRED
- PROPAGATION_SUPPORTS
- PROPAGATION_MANDATORY
- PROPAGATION_REQUIRES_NEW
- PROPAGATION_NOT_SUPPORTED
- PROPAGATION_NEVER
- PROPAGATION_NESTED
- Spring事務的隔離級別
- Spring事務基本配置樣例
數據庫事務
事務是什么
是數據庫操作的最小工作單元,是作為單個邏輯工作單元執行的一系列操作;這些操作作為一個整體一起向系統提交,要么都執行、要么都不執行;事務是一組不可再分割的操作集合。
事務的四大特性
原子性
事務是數據庫的邏輯工作單位,事務中包含的各操作要么都做,要么都不做
一致性
事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。
隔離性
一個事務的執行不能被其它事務干擾。即一個事務內部的操作及使用的數據對其它并發事務是隔離的,并發執行的各個事務之間不能互相干擾。
持續性
也稱永久性,指一個事務一旦提交,它對數據庫中的數據的改變就是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。
MySQL事務隔離級別
| Read-Uncommitted | 0 | 導致臟讀 |
| Read-Committed | 1 | 避免臟讀,允許不可重復讀和幻讀 |
| Repeatable-Read | 2 | MySQL默認的隔離級別。避免臟讀,不可重復讀,允許幻讀 |
| Serializable | 3 | 串行化讀,事務只能一個一個執行,避免了 臟讀、不可重復讀、幻讀。執行效率慢,使用時慎重 |
1. 臟讀
一個事務對數據進行了增刪改查,但是未提交事務。另一個事物可以讀取到未提交的數據,如果第一個事務進行了回滾,那么第二個事務就讀到了臟數據。
例子:
領導給張三發工資,10000元已打到張三賬戶,但該事務還未提交,正好這時候張三去查詢工資,發現10000元已到賬。這時領導發現張三工資算多了5000元,于是回滾了事務,修改了金額后將事務提交。最后張三實際到賬的只有5000元。
2. 不可重復度
一次事務發生了兩次讀操作,兩個讀操作之間發生了另一個事務對數據修改操作,這時候第一次和第二次讀到的數據不一致。
不可重復度關注點在數據更新和刪除,通過行級鎖可以實現可重復讀的隔離級別。
例子:
張三需要轉正1000元,系統讀到卡余額有2000元,此時張三老婆正好需要轉正2000元,并且在張三提交事務前把2000元轉走了,當張三提交轉賬是系統提示余額不足。
3. 幻讀
幻讀,指的是當某個事務在讀取某個范圍內的記錄時,另外一個事務又在該范圍內插入了新的記錄,當之前的事務再次讀取該范圍的記錄時,會產生幻行。
相對于不可重復讀,幻讀更關注其它事務的新增數據。通過行級鎖可以避免不可重復讀,但無法解決幻讀的問題,想要解決幻讀,只能通過Serializable隔離級別來實現。
例子:
張三老婆準備打印張三這個月的信用卡消費記錄,經查詢發現消費了兩次共1000元,而這時張三剛按摩完準備結賬,消費了1000元,這時銀行記錄新增了一條1000元的消費記錄。當張三老婆將消費記錄打印出來時,發現總額變為了2000元,這讓張三老婆很詫異。
4. 串行化讀
Serializable是最高的隔離級別,性能很低,一般很少用。在這級別下,事務是串行順序執行的,不僅避免了臟讀、不可重復讀,還避免了幻讀。
查看MySQL當前事務隔離級別
MySQL InnoDB默認的事務隔離級別為REPEATABLE-READ
mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+MySQL默認操作模式為自動提交模式
除非顯示的開啟一個事務,否則每個查詢都被當成一個單獨的事務自動執行。可以通脫設置autocommit的值改變默認的提交模式。
JDBC處理事務
Connection connection = null; PreparedStatement pstmt = null; ResultSet resultSet = null;try {Class.forName("com.mysql.jdbc.Driver");connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname?characterEncoding=utf-8","username", "password");connection.setAutoCommit(false);// others ......connection.commit(); } catch (Exception e) {connection.rollback(); } finally {connection.setAutoCommit(true);// close connection }Spring事務
Spring事務本質是對數據庫事務的支持,如果數據庫不支持事務(例如MySQL的MyISAM引擎不支持事務),則Spring事務也不會生效。
Spring的事務傳播
事務傳播行為是指一個事務方法A被另一個事務方法B調用時,這個事務A應該如何處理。事務A應該在事務B中運行還是另起一個事務,這個有事務A的傳播行為決定。
事務傳播屬性定義TransactionDefinition
int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6;| PROPAGATION_REQUIRED | 支持當前事務,如果當前沒有事務,就新建一個事務。這是Spring 默認的事務的傳播。 |
| PROPAGATION_SUPPORTS | 支持當前事務,如果當前沒有事務,就以非事務方式執行。 |
| PROPAGATION_MANDATORY | 支持當前事務,如果當前沒有事務,就拋出異常。 |
| PROPAGATION_REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。新建的事務將和被掛起的事務沒有任何關系,是兩個獨立的事務,外層事務失敗回滾之后, 不能回滾內層事務執行的結果,內層事務失敗拋出異常,外層事務捕獲, 也可以不處理回滾操作。 使用JtaTransactionManager作為事務管理器 |
| PROPAGATION_NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。使用JtaTransactionManager作為事務管理器 |
| PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則拋出異常。 |
| PROPAGATION_NESTED | 如果一個活動的事務存在,則運行在一個嵌套的事務中。如果沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個可以回滾的保存點。內部事務的回滾不會對外部事務造成影響。它只對DataSourceTransactionManager事務管理器起效。 |
PROPAGATION_REQUIRED
如果存在一個事務,則支持當前事務,如果沒有事務則開啟事務。
如下例子,單獨調用methodB時,當前上下文沒有事務,所以會開啟一個新的事務。
調用methodA方法時,因為當前上下文不存在事務,所以會開啟一個新的事務。當執行到methodB時,methodB發現當前上下文有事務,因此就加入到當前事務A中來。
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {methodB();// do something }@Transactional(propagation = Propagation.REQUIRED) public void methodB() {// do something }PROPAGATION_SUPPORTS
如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行.
單獨的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {methodB();// do something }// 事務屬性為SUPPORTS @Transactional(propagation = Propagation.SUPPORTS) public void methodB() {// do something }PROPAGATION_MANDATORY
如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。
當單獨調用methodB時,因為當前沒有一個活動的事務,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”)
當調用methodA時,methodB則加入到methodA的事務中,以事務方式執行。
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {methodB();// do something }@Transactional(propagation = Propagation.MANDATORY) public void methodB() {// do something }PROPAGATION_REQUIRES_NEW
使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。
它會開啟一個新的事務。如果一個事務已經存在,則先將這個存在的事務掛起。
從下面代碼可以看出,事務B與事務A是兩個獨立的事務,互不相干。事務B是否成功并不依賴于 事務A。如果methodA方法在調用methodB方法后的doSomeThingB方法失敗了,而methodB方法所做的結果依然被提交。而除了 methodB之外的其它代碼導致的結果卻被回滾了
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {doSomeThingA();methodB();doSomeThingB();// do something else }@Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() {// do something }當調用methodA(),相當于
public static void main(){TransactionManager tm = null;try{//獲得一個JTA事務管理器tm = getTransactionManager();tm.begin();//開啟一個新的事務Transaction ts1 = tm.getTransaction();doSomeThing();tm.suspend();//掛起當前事務try{tm.begin();//重新開啟第二個事務Transaction ts2 = tm.getTransaction();methodB();ts2.commit();//提交第二個事務} Catch(RunTimeException ex) {ts2.rollback();//回滾第二個事務} finally {//釋放資源}//methodB執行完后,恢復第一個事務tm.resume(ts1);doSomeThingB();ts1.commit();//提交第一個事務} catch(RunTimeException ex) {ts1.rollback();//回滾第一個事務} finally {//釋放資源} }PROPAGATION_NOT_SUPPORTED
總是非事務地執行,并掛起任何存在的事務。
使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。
PROPAGATION_NEVER
總是非事務地執行,如果存在一個活動事務,則拋出異常。
PROPAGATION_NESTED
如果一個活動的事務存在,則運行在一個嵌套的事務中。
如果沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。
這是一個嵌套事務,使用JDBC3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。 需要JDBC 驅動的java.sql.Savepoint類。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true(屬性值默認為false)。
@Transactional(propagation = Propagation.REQUIRED) methodA(){doSomeThingA();methodB();doSomeThingB(); }@Transactional(propagation = Propagation.NEWSTED) methodB(){// do something }單獨調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,則相當于:
main(){Connection con = null;Savepoint savepoint = null;try{con = getConnection();con.setAutoCommit(false);doSomeThingA();savepoint = con2.setSavepoint();try{methodB();} catch(RuntimeException ex) {con.rollback(savepoint);} finally {//釋放資源}doSomeThingB();con.commit();} catch(RuntimeException ex) {con.rollback();} finally {//釋放資源} }當methodB方法調用之前,調用setSavepoint方法,保存當前的狀態到savepoint。如果methodB方法調用失敗,則恢復到之前保存的狀態。
需要注意的是,這時的事務并沒有進行提交,如果后續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的所有操作。嵌套事務一個非常重要的概念就是內層事務依賴于外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗并不會引起外層事務的回滾。
Spring事務的隔離級別
事務隔離級別定義TransactionDefinition
int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8;| ISOLATION_DEFAULT | 這是個 PlatfromTransactionManager 默認的隔離級別, 使用數據庫默認的事務隔離級別。另外四個與 JDBC 的 隔離級別相對應。 |
| ISOLATION_READ_UNCOMMITTED | 這是事務最低的隔離級別,它允許另外一個事務可以看 到這個事務未提交的數據。這種隔離級別會產生臟讀, 不可重復讀和幻像讀。 |
| ISOLATION_READ_COMMITTED | 保證一個事務修改的數據提交后才能被另外一個事務讀 取。另外一個事務不能讀取該事務未提交的數據。 ISOLATION_REPEATABLE_READ |
| ISOLATION_SERIALIZABLE | 這是花費最高代價但是最可靠的事務隔離級別。事務被 處理為順序執行。 |
Spring事務基本配置樣例
<aop:aspectj-autoproxy proxy-target-class="true"/><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/> </bean><tx:advice id="transactionAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/><tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/><tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/><tx:method name="login" propagation="NOT_SUPPORTED"/><tx:method name="query*" read-only="true"/></tx:attributes> </tx:advice><aop:config><aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/><aop:aspect ref="dataSource"><aop:pointcut id="transactionPointcut" expression="execution(public * com.gupaoedu..*.service..*Service.*(..))" /></aop:aspect> </aop:config>總結
以上是生活随笔為你收集整理的Spring事务和MySQL事务详解面试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL事务实现原理详解
- 下一篇: MySQL事务日志