事務(wù)的基本原理Spring事務(wù)的本質(zhì)其實就是數(shù)據(jù)庫對事務(wù)的支持,,使用JDBC的事務(wù)管理機制,就是利用java.sql.Connection對象完成對事務(wù)的提交,那在沒有Spring幫我們管理事務(wù)之前,,我們要怎么做。 Connection conn = DriverManager.getConnection();
try {
conn.setAutoCommit(false); //將自動提交設(shè)置為false
執(zhí)行CRUD操作
conn.commit(); //當兩個操作成功后手動提交
} catch (Exception e) {
conn.rollback(); //一旦其中一個操作出錯都將回滾,,所有操作都不成功
e.printStackTrace();
} finally {
conn.colse();
}
事務(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ā)所可能存在的問題: 我們可以在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;
翻譯過來這幾個常量就是 隔離級別越高,意味著數(shù)據(jù)庫事務(wù)并發(fā)執(zhí)行性能越差,,能處理的操作就越少,。你可以通過conn.setTransactionLevel去設(shè)置你需要的隔離級別,。 了解了基本的JDBC事務(wù),那有了Spring,在事務(wù)管理上會有什么新的改變呢,? Spring事務(wù)管理Spring事務(wù)管理的核心接口是PlatformTransactionManager int getIsolationLevel();// 返回事務(wù)的隔離級別
String getName();// 返回事務(wù)的名稱
int getPropagationBehavior();// 返回事務(wù)的傳播行為
int getTimeout(); // 返回事務(wù)必須在多少秒內(nèi)完成
boolean isReadOnly(); // 事務(wù)是否只讀,,事務(wù)管理器能夠根據(jù)這個返回值進行優(yōu)化,,確保事務(wù)是只讀的
Spring事務(wù)的傳播屬性由上圖可知,Spring定義了7個以PROPAGATION_開頭的常量表示它的傳播屬性,。
Spring事務(wù)的隔離級別
調(diào)用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現(xiàn) void flush();//如果適用的話,,這個方法用于刷新底層會話中的修改到數(shù)據(jù)庫,,例如,,所有受影響的Hibernate/JPA會話。
boolean hasSavepoint(); // 是否有恢復(fù)點
boolean isCompleted();// 是否已完成
boolean isNewTransaction(); // 是否是新的事務(wù)
boolean isRollbackOnly(); // 是否為只回滾
void setRollbackOnly(); // 設(shè)置為只回滾
可以看出返回的結(jié)果是一些事務(wù)的狀態(tài),,可用來檢索事務(wù)的狀態(tài)信息。 配置事務(wù)管理器介紹完Spring事務(wù)的管理的流程大概是怎么走的,。接下來可以動手試試Spring是如何配置事務(wù)管理器的 <!-- 配置事務(wù)管理器 -->
<bean id='transactionManager' class='org.springframework.jdbc.datasource.DataSourceTransactionManager'>
<property name='dataSource' ref='dataSource' />
</bean>
這配置不是唯一的,,可以根據(jù)自己項目選擇的數(shù)據(jù)訪問框架靈活配置事務(wù)管理器 配置了事務(wù)管理器后,事務(wù)當然還是得我們自己去操作,,Spring提供了兩種事務(wù)管理的方式:編程式事務(wù)管理和聲明式事務(wù)管理,,讓我們分別看看它們是怎么做的吧。 編程式事務(wù)管理編程式事務(wù)管理我們可以通過PlatformTransactionManager實現(xiàn)來進行事務(wù)管理,,同樣的Spring也為我們提供了模板類TransactionTemplate進行事務(wù)管理,,下面主要介紹模板類,我們需要在配置文件中配置 <!--配置事務(wù)管理的模板-->
<bean id='transactionTemplate' class='org.springframework.transaction.support.TransactionTemplate'>
<property name='transactionManager' ref='transactionManager'></property>
<!--定義事務(wù)隔離級別,-1表示使用數(shù)據(jù)庫默認級別-->
<property name='isolationLevelName' value='ISOLATION_DEFAULT'></property>
<property name='propagationBehaviorName' value='PROPAGATION_REQUIRED'></property>
</bean>
TransactionTemplate幫我們封裝了許多代碼,,節(jié)省了我們的工作,。下面我們寫個單元測試來測測。 //方便測試直接寫的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();
}
dao對應(yīng)的sum方法 <select id='sum' resultType='java.lang.Integer'>
SELECT SUM(money) FROM tbl_account;
</select>
下面看看測試代碼 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ù)管理器當前事務(wù)需要回滾
status.setRollbackOnly();
e.printStackTrace();
}
}
});
System.out.println('after transaction');
Integer sum2 = baseSevice.sum();
System.out.println('after transaction sum: ' sum2);
}
}
當baseSevice.insert的第二個參數(shù)為false時,,我們假設(shè)插入數(shù)據(jù)沒有出現(xiàn)任何問題,測試結(jié)果如圖所示: 聲明式事務(wù)管理聲明式事務(wù)管理有兩種常用的方式,,一種是基于tx和aop命名空間的xml配置文件,一種是基于@Transactional注解,,隨著Spring和Java的版本越來越高,,大家越趨向于使用注解的方式,下面我們兩個都說,。 <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>
測試代碼 @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);
}
事務(wù)正常執(zhí)行結(jié)果截圖 <!-- 聲明式事務(wù)管理 配置事物的注解方式注入-->
<tx:annotation-driven transaction-manager='transactionManager'/>
然后在需要事務(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!!!');
}
}
rollbackFor屬性指定出現(xiàn)Exception異常的時候回滾,,遇到檢查性的異常需要回滾,,默認情況下非檢查性異常,包括error也會自動回滾,。 |
|
來自: liang1234_ > 《spring》