註解配置
(1)
不管是註解配置方式,還是 XML 配置方式,都需要在配置文件中配置事務管理器。
事務管理器的主要實現有:
- DataSourceTransactionManager:在應用程序中只需要處理一個數據源,而且通過 JDBC 存取。
- JtaTransactionManager:在 JavaEE 應用服務器上用 JTA(Java Transaction API) 進行事務管理。
- HibernateTransactionManager:用 Hibernate 框架存取數據庫。
<!-- 1. 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
(2)
在對應的方法上加上 @Transactional
註解。在 @Transactional
註解中還可以配置事務的屬性,比如事務的傳播行爲、事務的隔離級別、是否只讀、超時時間等。
-
事務的傳播行爲
在@Transactional
註解的propagation
屬性中定義。
事務的傳播行爲指的是:當一個事務方法被另一個事務方法調用的時候,是用另一個方法的事務,還是將另一個方法的事務掛起,自己新開一個事務呢?Spring 定義了 7 種傳播行爲:
最常用的是REQUIRED
和REQUIRED_NEW
,默認的傳播行爲時REQUIRED
。
① REQUIRED
當一個事務方法被另一個事務方法調用的時候,這種情況是使用另一個方法的事務。
② REQUIRED_NEW
這種情況是將另一個方法的事務掛起,而開啓一個新事務,並在自己的事務內運行。
這兩種情況具體的區別就是:比如我們在雙十一的時候要搶購東西,我們事先將這些商品加入到購物車中,但是在 0 點支付的時候,銀行卡中的錢不夠了,這時候事務的傳播行爲如果是REQUIRED
,則一件商品也買不到,而如果是REQUIRED_NEW
的話,則會買到一部分商品。 -
事務的隔離級別
在@Transactional
註解的isolation
屬性中定義。
MySQL 中有 4 中隔離級別,分別是:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。 -
哪些異常回滾,哪些異常不回滾
在rollbackFor
屬性中指定遇到時必須進行回滾的異常類型,可以爲多個;
在noRollbackFor
屬性中指定遇到時不回滾的異常類型,可以爲多個。
默認情況下捕獲到 RuntimeException 或 Error 時回滾,而捕獲到編譯時異常不回滾。 -
事務是否只讀
如果一個事物只讀取數據但不做修改,數據庫引擎可以對這個事務進行優化。
所以我們可以在readOnly
屬性中配置該事務是否是隻讀事務,從而幫助數據庫引擎優化事務。 -
事務的超時時長
如果一個事務長時間佔用資源,則會使整體的性能下降。
我們可以配置timeout
的屬性,用來指明當一個事務的運行時長超過我們配置的超時時長就會強制回滾,超時時長以秒爲單位。
下面來看一個示例:
/**
* 添加事務註解
* 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=false,
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);
}
(3)
配置了 @Transactional
註解之後,如何讓該註解起作用呢?
我們還需要在配置文件中開啓事務註解支持:
<!-- 啓用事務註解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
前提是在配置文件中加入 tx 命名空間。
XML 配置
當然我們也可以採用 XML配置的方式,也需要 3 個步驟:
(1)配置事務管理器
<!-- 1. 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
(2)配置事務的屬性
<!-- 2. 配置事務屬性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 根據方法名指定事務的屬性,如果沒有配置屬性則表示採用默認的屬性 -->
<tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException" read-only="false" timeout="5"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
(3)配置切入點表達式,表示事務應用在哪些方法上,並將切入點表達式和事務屬性關聯起來
<!-- 3. 配置事務切入點,,以及把事務切入點和事務屬性關聯起來 -->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.spring.tx.xml.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>