一、核心概念
1、概念
數據庫事務:數據庫事務( transaction)是訪問並可能操作各種數據項的一個數據庫操作序列,這些操作要麼全部執行,要麼全部不執行,是一個不可分割的工作單位。
事務概念擴展:事務概念來源於數據庫事務,擴展爲事務是一個由有限操作集合組成的邏輯單元,包括文件系統,消息隊列,一組不可分割的方法操作等。
事務操作的目的:
① 數據一致,指事務提交時保證事務內的所有操作都成功完成,並且更改永久生效;事務回滾時,保證能夠恢復到事務執行之前的狀態。
② 操作隔離,指多個同時執行的事務之間應該相互獨立,互不影響。
2、事務的四個特性:ACID
- 原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要麼全部完成,要麼完全不起作用。
- 一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的數據不應該被破壞。
- 隔離性(Isolation):可能有許多事務會同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞。
- 持久性(Durability):一旦事務完成,無論發生什麼系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來。通常情況下,事務的結果被寫到持久化存儲器中。
併發事務帶來的問題
- 丟失更新(Lost Update): 當兩個或多個事務選擇同一行,最初的事務修改的值,會被後面的事務修改的值覆蓋。
- 髒讀(Dirty Reads): 當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提 交到數據庫中,這時,另外一個事務也訪問這個數據,然後使用了這個數據。
- 不可重複讀(NonRepeatable Reads): 一個事務在讀取某些數據後的某個時間,再次讀取以前讀過的數據,卻發現和以前讀出的數據不一致。
- 幻讀(Phantom Reads): 一個事務按照相同的查詢條件重新讀取以前查詢過的數據,卻發現其他事務插入了滿足其查詢條件的新數據。
爲了解決上述提到的事務併發問題,數據庫提供一定的事務隔離機制來解決這個問題。MySQL的InnoDB引擎提供四種隔離級別(即ACID中的隔離性)
- 讀未提交(READ UNCOMMITTED),能解決幻讀問題問題;
- 讀已提交(READ COMMITTED),能解決不可重複讀取、幻讀問題;
- 可重複讀(REPEATABLE READ)(默認),能解決髒讀、不可重複讀、幻讀問題;
- 串行化(SERIALIZABLE)
InnoDB默認的隔離級別是REPEATABLE READ
,其可避免髒讀和不可重複讀,但不能避免幻讀。
3、傳播行爲(Spring針對方法嵌套調用時事務的創建行爲定義了七種事務傳播機制)
事務的第一個方面是傳播行爲(propagation behavior)。當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在自己的事務中運行。Spring定義了七種傳播行爲:
傳播行爲 | 含義 |
---|---|
PROPAGATION_REQUIRED |
表示當前方法必須運行在事務中。如果當前事務存在,方法將會在該事務中運行。否則,會啓動一個新的事務 |
PROPAGATION_SUPPORTS |
表示當前方法不需要事務上下文,但是如果存在當前事務的話,那麼該方法會在這個事務中運行 |
PROPAGATION_MANDATORY |
表示該方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常 |
PROPAGATION_REQUIRED_NEW |
表示當前方法必須運行在它自己的事務中。一個新的事務將被啓動。如果存在當前事務,在該方法執行期間,當前事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager |
PROPAGATION_NOT_SUPPORTED |
表示該方法不應該運行在事務中。如果存在當前事務,在該方法運行期間,當前事務將被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager |
PROPAGATION_NEVER |
表示當前方法不應該運行在事務上下文中。如果當前正有一個事務在運行,則會拋出異常 |
PROPAGATION_NESTED |
表示如果當前已經存在一個事務,那麼該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,那麼其行爲與PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行爲的支持是有所差異的。可以參考資源管理器的文檔來確認它們是否支持嵌套事務 |
4、事務執行原理(以mysql innodb引擎爲例)
1). redo log
redo log 即重做日誌,是用來實現事務的持久性。該日誌文件由兩部分組成:重做日誌緩衝(redo log buffer)以及重做日誌文件(redo log),前者是在內存中,後者在磁盤中。當事務提交之後會把所有修改信息都會存到該日誌中, 用於在刷新髒頁到磁盤時,發生錯誤時, 進行數據恢復使用。
start transaction; select balance from bank where name="Tom"; -- 生成 重做日誌 balance=8000 update bank set balance = balance - 2000; -- 生成 重做日誌 account=2000 update finance set account = account + 2000; commit;
執行流程如圖所示:
mysql 爲了提升性能不會把每次的修改都實時同步到磁盤,而是會先存到Buffer Pool(緩衝池)裏 頭,把這個當作緩存來用。然後使用後臺線程將緩存池刷新到磁盤。 當在執行刷新時,宕機或者斷電,可能會丟失部分數據。所以引入了redo log來記錄已成功提交事務的修改信息,並且在事務提交時會把redo log持久化到磁盤,系統重啓之後在讀取redo log恢復最新數據。 簡單來說 , redo log是用來恢復數據的用於保障,已提交事務的持久化特性 ;
2). undo log
undo log 即回滾日誌,用於記錄數據被修改前的信息。他正好跟前面所說的重做日誌所記錄的相 反,重做日誌記錄數據被修改後的信息。undo log主要記錄的是數據的邏輯變化,爲了在發生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,然後在發生錯誤時纔可以回滾。
undo log 記錄事務修改之前版本的數據信息,因此假如由於系統錯誤或者rollback操作而回滾的話 可以根據undo log的信息來進行回滾到沒被修改前的狀態。
5、spring事務原理
純JDBC操作數據庫事務步驟:
- 獲取連接 Connection con = DriverManager.getConnection()
- 開啓事務con.setAutoCommit(true/false);
- 執行CRUD
- 提交事務/回滾事務 con.commit() / con.rollback();
- 關閉連接 conn.close();
使用Spring的事務管理功能後,步驟 2 和 4 的代碼,而是由Spirng 自動完成。以註解方式爲例,配置文件開啓註解驅動,在相關的類和方法上通過註解@Transactional標識。Spring 在啓動的時候會去解析生成相關的bean,這時候會查看擁有相關注解的類和方法,並且爲這些類和方法生成代理,並根據@Transaction的相關參數進行相關配置注入,這樣就在代理中增加了事務代碼邏輯(開啓正常提交事務,異常回滾事務)。而實質實現事務功能是通過數據庫的事務處理(見上文,4、事務執行原理)。
二、應用場景
1、JDBC事務
在JDBC中處理事務,都是通過Connection完成的。同一事務中所有的操作,都在使用同一個Connection對象。
Connection的三個方法與事務有關:
- setAutoCommit(boolean):設置是否爲自動提交事務,如果true(默認值爲true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置爲false,那麼相當於開啓了事務了;
- commit():提交結束事務。
- rollback():回滾結束事務。
JDBC處理事務的代碼格式:
try{ con.setAutoCommit(false);//開啓事務 ...... con.commit();//try的最後提交事務 } catch() { con.rollback();//回滾事務 }
eg:
import cn.itcast.jdbc.JdbcUtils; import org.junit.Test; import java.sql.Connection; import java.sql.SQLException; public class Demo1 { /* * 演示轉賬方法 * 所有對Connect的操作都在Service層進行的處理 * 把所有connection的操作隱藏起來,這需要使用自定義的小工具(day19_1) * */ public void transferAccounts(String from,String to,double money) { //對事務的操作 Connection con = null; try{ con = JdbcUtils.getConnection(); con.setAutoCommit(false); AccountDao dao = new AccountDao(); dao.updateBalance(con,from,-money);//給from減去相應金額 if (true){ throw new RuntimeException("不好意思,轉賬失敗"); } dao.updateBalance(con,to,+money);//給to加上相應金額 //提交事務 con.commit(); } catch (Exception e) { try { con.rollback(); } catch (SQLException e1) { e.printStackTrace(); } throw new RuntimeException(e); } } @Test public void fun1() { transferAccounts("zs","ls",100); } }
補充:
JDBC事務優缺點:JDBC爲使用Java進行數據庫的事務操作提供了最基本的支持。通過JDBC事務,我們可以將多個SQL語句放到同一個事務中,保證其ACID特性。但是,一個 JDBC 事務不能跨越多個數據庫,不支持多數據庫的操作或分佈式場景。
2、JTA事務
JTA(Java Transaction API)是一種高層的,與實現無關的,與協議無關的API,應用程序和應用服務器可以使用JTA來訪問事務。JTA提供了跨數據庫連接的事務管理能力。JTA事務管理則由JTA容器實現,J2ee框架中事務管理器與應用程序,資源管理器,以及應用服務器之間的事務通訊。一個分佈式事務包括一個事務管理器和一個或多個資源管理器。
1)JTA的構成
a、高層應用事務界定接口,供事務客戶界定事務邊界的
b、X/Open XA協議(資源之間的一種標準化的接口)的標準Java映射,它可以使事務性的資源管理器參與由外部事務管理器控制的事務中
c、高層事務管理器接口,允許應用程序服務器爲其管理的應用程序界定事務的邊界
2)JTA的主要接口位於javax.transaction包中
a、UserTransaction接口:讓應用程序得以控制事務的開始、掛起、提交、回滾等。由Java客戶端程序或EJB調用。
b、TransactionManager 接口:用於應用服務器管理事務狀態
c、Transaction接口:用於執行相關事務操作
d、XAResource接口:用於在分佈式事務環境下,協調事務管理器和資源管理器的工作
e、Xid接口:爲事務標識符的Java映射
補充:
JTA提供了分佈式事務的解決方案,嚴格的ACID。但是JTA實現複雜,通常情況下,JTA UserTransaction需要從JNDI獲取,如果使用JTA,就需要同時使用JTA和JNDI。
3、容器事務:主要指的是J2EE應用服務器提供的事務管理,如在Spring、Hibernate等框架中都有各自的事務管理功能,表現形式不同,但都是在JAVA事務管理的基礎上實現的。
Spring事務
Spring並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。
Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager,通過這個接口,Spring爲各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。此接口的內容如下:
Public interface PlatformTransactionManager()...{ // 由TransactionDefinition得到TransactionStatus對象 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交 Void commit(TransactionStatus status) throws TransactionException; // 回滾 Void rollback(TransactionStatus status) throws TransactionException; }
所以Spring事務管理的一個優點就是爲不同的事務API提供一致的編程模型,如JTA、JDBC、Hibernate、JPA。
Spring事務抽象的核心類圖
部分Spring包含的對PlatformTransactionManager
的實現類如下圖所示:
AbstractPlatformTransactionManager
抽象類實現了Spring事務的標準流程,其子類DataSourceTransactionManager
是我們使用較多的JDBC單數據源事務管理器,而JtaTransactionManager
是JTA(Java Transaction API)規範的實現類,另外兩個則分別是JavaEE容器WebLogic和WebSphere的JTA事務管理器的具體實現。
spring事務核心邏輯
事務攔截器TransactionInterceptor
在invoke
方法中,通過調用父類TransactionAspectSupport
的invokeWithinTransaction
方法進行事務處理,該方法支持聲明式事務和編程式事務。
// TransactionInterceptor.class @Override public Object invoke(final MethodInvocation invocation) throws Throwable { // 獲取targetClass ... // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() { @Override public Object proceedWithInvocation() throws Throwable { // 實際執行目標方法 return invocation.proceed(); } }); } // TransactionInterceptor父類TransactionAspectSupport.class protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. // 查詢目標方法事務屬性、確定事務管理器、構造連接點標識(用於確認事務名稱) final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 事務獲取 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // 通過回調執行目標方法 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 目標方法執行拋出異常,根據異常類型執行事務提交或者回滾操作 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { // 清理當前線程事務信息 cleanupTransactionInfo(txInfo); } // 目標方法執行成功,提交事務 commitTransactionAfterReturning(txInfo); return retVal; } else { // 帶回調的事務執行處理,一般用於編程式事務 ... } }
TransactionAspectSupport
//TransactionAspectSupport.class protected TransactionInfo createTransactionIfNecessary( PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { ... TransactionStatus status = null; if (txAttr != null) { if (tm != null) { // 獲取事務 status = tm.getTransaction(txAttr); ... } protected void commitTransactionAfterReturning(TransactionInfo txInfo) { if (txInfo != null && txInfo.hasTransaction()) { ... // 提交事務 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } } protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.hasTransaction()) { ... if (txInfo.transactionAttribute.rollbackOn(ex)) { try { // 異常類型爲回滾異常,執行事務回滾 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } ... } else { try { // 異常類型爲非回滾異常,仍然執行事務提交 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } ... } protected final class TransactionInfo { private final PlatformTransactionManager transactionManager; ...
1)spring-jdbc
如果應用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會處理事務邊界。爲了使用DataSourceTransactionManager,需要使用如下的XML將其裝配到應用程序的上下文定義中:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
實際上,DataSourceTransactionManager是通過調用java.sql.Connection來管理事務,而後者是通過DataSource獲取到的。通過調用連接的commit()方法來提交事務,同樣,事務失敗則通過調用rollback()方法進行回滾。
2)Hibernate事務
如果應用程序的持久化是通過Hibernate實現的,那麼需要使用HibernateTransactionManager。對於Hibernate3,需要在Spring上下文定義中添加如下的<bean>
聲明:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
sessionFactory屬性需要裝配一個Hibernate的session工廠,HibernateTransactionManager的實現細節是它將事務管理的職責委託給org.hibernate.Transaction對象,而後者是從Hibernate Session中獲取到的。當事務成功完成時,HibernateTransactionManager將會調用Transaction對象的commit()方法,反之,將會調用rollback()方法。
3)Java持久化API事務(JPA)
Hibernate多年來一直是事實上的Java持久化標準,但是現在Java持久化API作爲真正的Java持久化標準進入大家的視野。如果使用JPA的話,需要使用Spring的JpaTransactionManager來處理事務。需要在Spring中這樣配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
4)Java原生API事務
如果沒有使用以上所述的事務管理,或者是跨越了多個事務管理源(比如兩個或者是多個不同的數據源),就需要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManagerName" value="java:TransactionManager" /> </bean>
JpaTransactionManager只需要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory接口的任意實現)。JpaTransactionManager將與由工廠所產生的JPA EntityManager合作來構建事務。
三、Spring事務應用---編程式事務和聲明式事務
區別:Spring提供了對編程式事務和聲明式事務的支持,編程式事務允許用戶在代碼中精確定義事務的邊界,而聲明式事務(基於AOP)有助於用戶將操作與事務規則進行解耦(對原有代碼無侵入)。
(1)編程式事務
Spring提供兩種方式的編程式事務管理,分別是:使用TransactionTemplate和直接使用PlatformTransactionManager。
1.1)使用TransactionTemplate
採用TransactionTemplate和採用其他Spring模板,如JdbcTempalte和HibernateTemplate是一樣的方法。它使用回調方法,把應用程序從處理取得和釋放資源中解脫出來。如同其他模板,TransactionTemplate是線程安全的。代碼片段:
TransactionTemplate tt = new TransactionTemplate(); // 新建一個TransactionTemplate Object result = tt.execute( new TransactionCallback(){ public Object doTransaction(TransactionStatus status){ updateOperation(); return resultOfUpdateOperation(); } }); // 執行execute方法進行事務管理
使用TransactionCallback()可以返回一個值。如果使用TransactionCallbackWithoutResult則沒有返回值。
1.2)使用PlatformTransactionManager
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定義一個某個框架平臺的TransactionManager,如JDBC、Hibernate dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 設置數據源 DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定義事務屬性 transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 設置傳播行爲屬性 TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 獲得事務狀態 try { // 數據庫操作 dataSourceTransactionManager.commit(status);// 提交 } catch (Exception e) { dataSourceTransactionManager.rollback(status);// 回滾 }
(2)聲明式事務
①聲明式事務原理
聲明式事務的實現就是通過環繞增強的方式,在目標方法執行之前開啓事務,在目標方法執行之後提交或者回滾事務,事務攔截器的繼承關係圖可以體現這一點:
②用法:根據代理機制的不同,總結了五種Spring事務的配置方式,配置文件如下:
1)每個Bean都有一個代理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
2)所有Bean共享一個代理基類
3)使用攔截器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 配置DAO --> <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
4)使用tx標籤配置的攔截器
5)全註解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
此時在DAO上需加上@Transactional註解,如下:
import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } }
eg:
數據庫表:
book(isbn, book_name, price)
account(username, balance)
book_stock(isbn, stock)
xml配置:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <import resource="applicationContext-db.xml" /> <context:component-scan base-package="com.springinaction.transaction"> </context:component-scan> <tx:annotation-driven transaction-manager="txManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
BookShopServiceImpl
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; /** * 1.添加事務註解 * 使用propagation 指定事務的傳播行爲,即當前的事務方法被另外一個事務方法調用時如何使用事務。 * 默認取值爲REQUIRED,即使用調用方法的事務 * REQUIRES_NEW:使用自己的事務,調用的事務方法的事務被掛起。 * * 2.使用isolation 指定事務的隔離級別,最常用的取值爲READ_COMMITTED * 3.默認情況下 Spring 的聲明式事務對所有的運行時異常進行回滾,也可以通過對應的屬性進行設置。通常情況下,默認值即可。 * 4.使用readOnly 指定事務是否爲只讀。 表示這個事務只讀取數據但不更新數據,這樣可以幫助數據庫引擎優化事務。若真的是一個只讀取數據庫值得方法,應設置readOnly=true * 5.使用timeOut 指定強制回滾之前事務可以佔用的時間。 */ @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, noRollbackFor={UserAccountException.class}, readOnly=true, timeout=3) @Override public void purchase(String username, String isbn) { //1.獲取書的單價 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新書的庫存 bookShopDao.updateBookStock(isbn); //3.更新用戶餘額 bookShopDao.updateUserAccount(username, price); } }
備註:和spring boot集成使用方式參考 https://zhuanlan.zhihu.com/p/227922586
四、分佈式事務
1、5種分佈式事務解決方案原理 https://developer.51cto.com/art/201907/600249.htm
2、分佈式事務框架-Seata
(1)簡介
Seata 是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata 將爲用戶提供了 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。
(2)ShardingSphere 集成了 SEATA作爲分佈式事務解決方案
Apache ShardingSphere ,是一套開源的分佈式數據庫解決方案組成的生態圈,包括由 JDBC、Proxy 和 Sidecar(規劃中)。TCC 和 Saga 是兩種常見分佈式事務實現方案, 主張開發者自行實現對數據庫的反向操作,來達到數據在回滾時仍能夠保證最終一致性。 SEATA 實現了 SQL 反向操作的自動生成,可以使柔性事務不再必須由開發者介入才能使用。ShardingSphere 集成了 SEATA 作爲柔性事務的使用方案。
3、分佈式事務框架-Atomikos
採用微服務之間強一致性實現方式。執行原理如圖所示:
感謝閱讀,借鑑了不少大佬資料,如需轉載,請註明出處,謝謝!https://www.cnblogs.com/huyangshu-fs/p/15600093.html