1. 程式人生 > >資料庫--事務

資料庫--事務

個人分類: Java

首先,什麼是事務?

事務就是業務上的一個邏輯單元,它能夠保證其中對資料所有的操作,要麼成功,要麼失敗。

其次,事務的特性有哪些?

1.原子性。

例如,轉賬,A賬戶減少,B賬戶增加。雖然是兩條 DML語句,但是被當做是一個整體,一次事務。兩條語句只能同時成功或者同時失敗。

2.一致性。

賬戶A和B,要麼都是轉賬前的狀態,要麼都是轉賬後的狀態。(不能A賬戶的錢減少了但是B賬戶的錢沒有增加)。

3.隔離性。

雖然在某個時間段很多人都在轉賬,但是每個人的轉賬都是在一個自己的事務中,彼此不會影響。

4.永續性。

事務提交成功後,資料修改永遠生效。

在考慮事務的隔離級別之前,需要認識到如果不考慮事務的隔離性,會發生的異常情況:

1.髒讀

一個事務讀取了另外一個事務未提交的資料。(會對系統的併發處理帶來很大的隱患)

2.不可重複讀

在同一個事務內,多次讀同一個資料時,發現該資料已經 被另一個已經提交的事務修改。(在一個事務內兩次讀到的資料時是不一樣的。)

3.幻讀

一個事務根據相同的查詢條件,重新執行查詢,返回的記錄中包含與前一次執行查詢返回的記錄不同的行。

以上這三種 情況都是同時進行的幾個事務對相同的資料進行讀取時造成的。

如何處理這幾種異常呢?

ANSI SQL-92標準中定義了以下幾種事務隔離級別:

1.Read Uncommitted

最低等級的事務隔離,僅僅保證讀取過程中不會讀到非法資料。(三種異常情況均可能發生)

2.Read Committed

避免了“髒讀”。一個select查詢只能檢視到查詢開始之前提交的資料。在查詢執行時,其他事務修改或者提交的資料它看不到(資料庫預設的事務隔離級別)

3.Repeatable Read

避免了“髒讀”和“不可重複讀”。一個事務不可能更新由另一個事務讀取但是沒有提交的資料。應用並不廣泛。因為它可能出現幻讀。同時帶來更多效能損失。

4.Serializable

ORACLE資料庫只支援Read Committed 和Serializable兩種隔離級別。另外自定義了一個Read Only的隔離級別,只允許讀,不允許改。避免不可重複讀和幻讀

三種都能避免。所有事務序列,而不是並行。

Spring事務的隔離級別  1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別.       另外四個與JDBC的隔離級別相對應  2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的資料。       這種隔離級別會產生髒讀,不可重複讀和幻像讀。  3. ISOLATION_READ_COMMITTED: 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料  4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。       它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。  5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。       除了防止髒讀,不可重複讀外,還避免了幻像讀。

使用Spring AOP實現宣告式事務管理

1.基於XML配置(使用較多)

(1)配置事務管理類

  1.  <!-- 定義事務管理器 -->  

  2. <bean id="transactionManager"  

  3.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  

  4.     <property name="dataSource" ref="dataSource" />  

  5. </bean>  

在spring的配置中配置資料來源(dataSource)、事務管理器,事務管理器使用不同的orm框架事務管理器類就不同,mybatis 是org.springframework.jdbc.datasource.DataSourceTransactionManager  。而hibernate事務管理器為org.springframework.orm.hibernate3.HibernateTransactionManager  

(2)配置事務屬性

  1. <!-- 配置事務的屬性 -->  

  2.     <tx:advice id="TestAdvice" transaction-manager="transactionManager">  

  3.         <!--配置事務傳播性,隔離級別以及超時回滾等問題 -->  

  4.         <tx:attributes>  

  5.             <tx:method name="search*" propagation="REQUIRED" read-only="true" isolation="DEFAUT" TIMEOUT="-1" />  

  6.             <tx:method name="del*" propagation="REQUIRED" />  

  7.             <tx:method name="update*" propagation="REQUIRED" />  

  8.             <tx:method name="add*" propagation="REQUIRED" />  

  9.         </tx:attributes>  

  10.     </tx:advice>

事務屬性在<tx:method>中進行設定,Spring支援對不同的方法設定不同的事務屬性,所以可以為一個<tx:advice>設定多個<tx:method>,其中name屬性指定匹配的方法(這裡需要對這些方法名進行約定,如果事務切入點在service上,則最好和Dao的方法命名區分開,也不要使用get set關鍵字,防止和屬性的getter setter發生混淆)

事務有以下幾個常用屬性:

a.read-only:設定該事務中是否允許修改資料。(對於只執行查詢功能的事務,設定為TRUE可以提高事務的執行速度)  

b.propagation:事務的傳播機制。一般設定為required。可以保證在事務中的程式碼只在當前事務中執行,防止建立多個事務。

c.isolation:事務隔離級別。不是必須的。預設值是default。

d.timeout:允許事務執行的最長時間,以秒為單位。

e.rollback-for:觸發回滾的異常。

f.no-rollback-for:不會觸發回滾的異常。

***實際開發中,對於只執行查詢功能的事務,要設定read-only為TRUE,其他屬性一般使用預設值即可。

(3)配置事務的AOP切入點

  1. <aop:config>  

  2.         <!--配置事務切點 -->  

  3.         <aop:pointcut id="services"  

  4.             expression="execution(public* com.pb.service.*.*(..))" />  

  5.         <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />  

  6.     </aop:config>  

該設定的含義是:對於com.pb.service.impl包及子包下的所有類的所有公共方法進行切入。(被切入的 方法經過<tx:method>篩選)web應用程式最合適的事務切入點是Service的方法上。

----通過以上三個步驟設定好宣告式事務後,當Service中 的業務方法被呼叫之前,Spring會獲取事務物件並啟動事務。並使用try-catch-finally來處理異常。業務方法執行成功則會提交事務,預設情況下如果丟擲了RuntimeException 或者Rrror 物件就會回滾事務。(注意: 這裡注意一下,在tx:method中配置了rollback_for 中配置的Exception 這個是執行時的異常才會回滾不然其他異常是不會回滾的!)

2.使用annotation配置

*1.在事務管理的dao實現類之前標註@Transactional

*2.在要進行事務管理的方法前加上@Transactional(propagation= Propagation.REQUIRED)

*3.在配置檔案中指定驅動:<tx:annotation-driven transaction-manager="transactionManager" />

  1. package demo.spring.dao;

  2. import java.util.Iterator;

  3. import java.util.List;

  4. import javax.sql.DataSource;

  5. import org.springframework.jdbc.core.JdbcTemplate;

  6. import org.springframework.transaction.annotation.Propagation;

  7. import org.springframework.transaction.annotation.Transactional;

  8. import demo.spring.entity.Person;

  9. @Transactional//將此類進行事務管理

  10. public class PersonDaoImpl implements PersonDao {

  11. private JdbcTemplate jt;

  12. public void setDataSource(DataSource dataSource){

  13. jt = new JdbcTemplate(dataSource);

  14. }

  15. @Override

  16. public void insert(long id, String name, int age) {

  17. jt.update("insert into person values('"+id+"','"+name+"','"+age+"')");

  18. }

  19. @Transactional(propagation= Propagation.REQUIRED)//定義要事務管理的方法,指定傳播行為

  20. public void batchInsert(List persons) {

  21. for(Iterator it = persons.iterator(); it.hasNext(); ){

  22. Person p = (Person) it.next();

  23. insert(p.getId(),p.getName(),p.getAge());

  24. }

  25. }

  26. }