Spring框架深入(三)--事務
一、事務的概念
1、事務是什麽
(1)、作為單個邏輯工作單元執行的一系列操作(一組SQL會成為一個事務),是並發控制的單位,要麽全部成功,要麽全部失敗
(2)、如銀行轉賬(需要兩個update)/網上商城購物
2、事務的特征
(1)、原子性:所有的操作會被看成一個邏輯單元,要麽全部成功,要麽全部失敗
(2)、一致性:事務在完成時,必須使所有的數據都保持一致狀態,保證數據的完整性
(3)、隔離性:與並發有關,事務之間的相互影響—隔離級別
(4)、持久性:事務結束後,結果是可以固化的
二、事務隔離
1、事務隔離用於處理事務之間的並發問題
2、事務的隔離級別
(1)、未授權讀取
(2)、授權讀取
(3)、可重復讀取
(4)、序列化:隔離級別最高的
3、事務隔離的實現:
(1)、悲觀鎖:基於數據庫的鎖,不能操作數據
a、悲觀鎖是讀取的時候為後面的更新加鎖,之後再來的讀操作都會等待。這種是數據庫鎖。
b、悲觀鎖是數據庫實現,他阻止一切數據庫操作。
c、在實際生產環境裏邊,如果並發量不大且不允許臟讀,可以使用悲觀鎖解決並發問題;
(2)、樂觀鎖:不同的事務可以看到同一對象的不同歷史版本
a、樂觀鎖是一種思想,是基於版本號機制的,具體實現是,表中有一個版本字段,第一次讀的時候,獲取到這個字段。處理完業務邏輯開始更新的時候,需要再次查看該字段的值是否和第一次的一樣。如果一樣更新,反之拒絕。之所以叫樂觀,因為這個模式沒有從數據庫加鎖。
b、樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量;
c、在實際生產環境中,如果系統的並發非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇使用樂觀鎖。
三、B/S中的事務
1、一個請求對應一個業務,一個業務其實就應該是一個事務
2、一個請求對應一個事務
3、一個事務----MyBatis中的事務與sqlSession相關
4、一個請求對應著啟動一個線程,一個線程對應一個事務
(1)、當前請求線程所執行的所有操作都是屬於同一個事務的,使用的是同一個sqlSession
(2)、dao的所有操作應該是基於同一個sqlSession,這些操作才構成一個事務;
5、一個線程對應著同一個sqlSession:
如何讓一個線程中得到的sqlSession對象是同一個呢?使用ThreadLocal
6、ThreadLocal
(1)、當前線程變量:key/value
(2)、將sqlSession放入線程上下文空間,線程會執行請求要做的所有方法(很多個dao操作),每次的dao操作所使用的sqlSession都從當前線程上下文取得;
(3)、使用原理/步驟:
a、每個dao在執行的時候,會使用getSqlSession來獲得會話
b、判斷當前線程中是否有session,如果有的話,就用當前線程的session。如果沒有的話,就會創建session放入當前線程,同時返回session;
c、繼續執行下一個dao操作的時候,因為是屬於同一個請求線程的,所以可以從當前線程裏拿到同一個session,從而形成事務的概念。
/* * 當前線程上下文空間 * 將session放入當前線程變量中 * 如果當前線程有該對象,就直接拿來用 * 如果沒有,才去新建對象 * */ public class SessionFactoryUtil { private static ThreadLocal threadLocal = new ThreadLocal(); public static SqlSession getSqlSession() { SqlSession session = (SqlSession) threadLocal.get(); if(session!=null) { return session; } else{ session=new SqlSession(); threadLocal.set(session); return session; } } }
public void run() { //這兩個dao操作時屬於同一個事務的,也就是dao操作所使用的sqlSession是同一個 userDao.buy(); productDao.updatePruduct(); }
7、B/S中要實現事務,需要將sqlSession放入ThreadLocal(當前線程上下文)中
通過servletFilter實現請求到達的時候,創建session放入ThreadLocal
四、Spring中的事務
1、事務其實是一個切面的存在,只需要在Spring中配置AOP就可以實現事務了
2、AOP
(1)、核心業務:自己編寫
(2)、切面:事務這個切面Spring已經提供了實現:不需要自己編寫
a、Spring已經提供了實現事務的通知,配置為bean
b、事務管理平臺:確定事務切面用在哪個平臺
c、事務策略定義:事務的屬性(隔離級別、傳播性)
d、事務狀態
3、在Spring中實現事務:通過配置AOP實現
(1)、將Spring提供的通知類配置到核心業務線
(2)、基於註解進行配置
(3)、基於AOP的配置文件進行配置
4、PlatformTransactionManager
確定要做的事務是基於哪個平臺(JDBC、Hibernate、MyBatis、JPA)
五、Spring+MyBatis事務管理
1、Spring+MyBatis集成後,默認每個操作都是產生一個新的SqlSession,不構成事務概念,每個操作就是一個獨立的事務
2、事務都是基於service層
3、Spring中事務AOP的配置
(1)、首先,我們這兒有一個Author類(實現類get,set方法),以及AuthorMapper接口
public class Author { private int id; private String username; private String password; private String email; private String bio; }
public interface AuthorMapper { public Author findById(int id); public void insertAuthor(Author aut); }
接口中有兩個方法,分別是查找所有和插入
如果我們就這樣運行,他就會創建兩個SqlSession分別處理兩個方法;
(3)、在核心配置文件中進行事務AOP的配置
<!-- AOP 事務配置 核心業務:service——已完成 切面bean:Spring提供支持類,進行配置 建立切入點aop關聯,進行配置 --> <!-- 配置事務管理器:切面的一部分 --> <bean id="transManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事務策略狀態:切面的一部分 事務通知的配置 --> <tx:advice id="txAdvice" transaction-manager="transManager"> <tx:attributes> <!-- 對應的方法與事務的使用 REQUIRED:在事務中執行,如果事務不存在,則會重新創建一個 SUPPORTS:使用當前的環境執行,如果當前存在事務,則會使用這個事務,如果當前沒有這個事務,則不使用事務 --> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <!-- 核心業務與事務關聯起來 --> <aop:config> <!-- 切入點 --> <aop:pointcut expression="execution(* service.*.*.*(..))" id="transPointCut"/> <!-- 關聯操作 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="transPointCut"/> </aop:config>
如果是對於hibernate而言,到這兒就已經可以實現事務了。但是,對於MyBatis而言,還需要繼續進行配置,有兩種配置方法;
a、基於註解實現事務
<!-- 配置註解驅動,標註@Transactional的類和方法都具有事務性 --> <tx:annotation-driven transaction-manager="transManager"/>
只需要在信心配置文件中配置註解驅動,並且在實現類上加上@Transactional就可以了;
@Transactional @Override public void doFindAndInsert() { Author aut=new Author(); aut.setBio("test"); aut.setEmail("test"); aut.setPassword("test"); aut.setUsername("test"); authorMapper.insertAuthor(aut); //報錯的時候,回滾 int i=5/0; Author author = authorMapper.findById(1); System.out.println(author); return null; }
運行結果:
b、基於事務管理模板實現事務
配置一個支持事務的模板bean
<!-- 基於事務模板 用於支持事務的模板bean --> <bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="transManager"></constructor-arg> </bean>
實現類:
public void doFindAndInsert() { txTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus arg0) { // TODO Auto-generated method stub Author aut=new Author(); aut.setBio("test"); aut.setEmail("test"); aut.setPassword("test"); aut.setUsername("test"); authorMapper.insertAuthor(aut); //報錯的時候,回滾 int i=5/0; Author author = authorMapper.findById(1); System.out.println(author); return null; } }); }
運行結果:
PS:因作者能力有限,如有誤還請諒解;
Spring框架深入(三)--事務