Spring中@Transactional事務回滾
一、Spring 預設事務
Spring中@Transactional
事務,預設情況下只對 RuntimeException
回滾。
即:
- 如果被註解的資料庫操作方法中發生了unchecked異常(RuntimeException),所有的資料庫操作將rollback;
- 如果發生的異常是checked異常(Exception),預設情況下資料庫操作還是會提交的。
也就是說,預設情況下,如果程式丟擲的是Exception 及 Exception的子類異常,Spring的@Transactional
事務 是不會回滾的。
如果想 unchecked異常,checked異常時都回滾,可以這樣配置:
@Transactional(rollbackFor={RuntimeException.class, Exception.class})
看例子:
//不指定異常,預設就是RuntimeException。丟擲Exception時,事務不會回滾。
@Transactional()
public void methodName() {
throw new Exception("註釋");
}
//預設就是RuntimeException,丟擲RuntimeException時,事務肯定會回滾。
@Transactional()
public void methodName() {
throw new RuntimeException("註釋");
}
//指定拋 Exception時回滾,遇到異常Exception時回滾,
//拋 RuntimeException 異常是不會回滾的
@Transactional(rollbackFor=Exception.class)
public void methodName() {
throw new Exception("註釋");
}
//指定不回滾,遇到執行期例外(throw new RuntimeException("註釋");)會回滾
@Transactional(noRollbackFor=Exception.class)
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("註釋");
}
二、 Spring + myBatis常用的事務配置
@Transactional的事務配置:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven proxy-target-class="false" transaction-manager="txManager" />
整合spring配置如下:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 讀取屬性檔案 -->
<context:property-placeholder
location="classpath:/properties/jdbc.properties,classpath:/properties/config.properties,classpath:/properties/rabbitmq-config.properties,classpath:/properties/redis.properties"
ignore-unresolvable="true" />
<!-- 阿里 druid資料庫連線池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!-- 資料庫基本資訊配置 -->
<property name="url" value="${mysql.url}" />
<property name="username" value="${mysql.username}" />
<property name="password" value="${mysql.password}" />
<property name="driverClassName" value="${mysql.driverClassName}" />
<property name="filters" value="${mysql.filters}" />
<!-- 最大併發連線數 -->
<property name="maxActive" value="${mysql.maxActive}" />
<!-- 初始化連線數量 -->
<property name="initialSize" value="${mysql.initialSize}" />
<!-- 配置獲取連線等待超時的時間 -->
<property name="maxWait" value="${mysql.maxWait}" />
<!-- 最小空閒連線數 -->
<property name="minIdle" value="${mysql.minIdle}" />
<!-- 最大空閒連線數 -->
<property name="maxIdle" value="${mysql.maxIdle}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/sqlmap/sqlmap-config.xml" />
<!-- 自動掃描mapping.xml檔案
<property name="mapperLocations" value="classpath:/sqlmap/po/*.xml,classpath:sqlmap/base/*.xml"></property> -->
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven proxy-target-class="false" transaction-manager="txManager" />
<!-- 自動掃描註解的bean -->
<context:component-scan base-package="com.aop8.springmvc_study.service" />
</beans>
三、Spring中 @Transactional 事務回滾注事項
-
在需要事務管理的地方加
@Transactional
註解。@Transactional
註解可以被應用於介面定義和介面方法、類定義和類的 public 方法上。 -
@Transactional
註解只能應用到public
可見度的方法上。如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不會展示已配置的事務設定。
-
注意僅僅
@Transactional
註解的出現不足於開啟事務行為,它僅僅 是一種元資料。必須在配置檔案中使用配置元素,才真正開啟了事務行為。 -
通過元素的
proxy-target-class
屬性值來控制是基於介面的還是基於類的代理被建立。如果
proxy-target-class
屬值被設定為false
或者這個屬性被省略,那麼標準的JDK基於介面的代理將起作用。
如果proxy-target-class
屬值被設定為true
,那麼基於類的代理將起作用(這時需要CGLIB庫)。 -
Spring團隊建議在具體的類(或類的方法)上使用
@Transactional
註解,而不要使用在類所要實現的任何介面上。在介面上使用 @Transactional 註解,只能當你設定了基於介面的代理時它才生效。因為註解是 不能繼承 的,這就意味著如果正在使用基於類的代理時,那麼事務的設定將不能被基於類的代理所識別,而且物件也將不會被事務代理所包裝。
-
@Transactional
的事務開啟,或者是基於介面的,或者是基於類的代理被建立。所以在同一個類中一個方法呼叫另一個方法有事務的方法,事務是不會起作用的。
原因:
spring 在掃描bean的時候會掃描方法上是否包含@Transactional
註解,如果包含,spring會為這個bean動態地生成一個子類(即代理類,proxy),代理類是繼承原來那個bean的。
此時,當這個有註解的方法被呼叫的時候,實際上是由代理類來呼叫的,代理類在呼叫之前就會啟動 transaction
。
然而,如果這個有註解的方法是被同一個類中的其他方法呼叫的,那麼該方法的呼叫並沒有通過代理類,而是直接通過原來的那個bean,所以就不會啟動transaction,我們看到的現象就是 @Transactional
註解無效
總結:
同一個類中,一個沒有事務的方法A,去呼叫另一個有事務的方法B時,因為是直接呼叫,而不是呼叫的代理類,所以事務不起用的。