1.上一章,我們談到了spring+mybatis聲明式事務管理,我們在文章末尾提到,在實際項目中,用得更多的是注解式事務管理,這一章將學習一下注解式事務管理的有關知識.注解式事務管理只需要在上一節的小實例上更改兩個文件就好:
(1)更改spring配置文件applicationContext.xml:
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd "> <!-- 加載配置文件 --> <context:property-placeholder location="config.properties" /> <!-- 指定spring注解注入層 --> <context:component-scan base-package="com.gnc" /> <!-- 數據庫連接池管理 --> <bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${db.driverClass}"></property> <property name="jdbcUrl" value="${db.jdbcUrl}"></property> <property name="user" value="${db.user}"></property> <property name="password" value="${db.password}"></property> <property name="initialPoolSize" value="${db.initialPoolSize}"></property> <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 --> <property name="maxIdleTime" value="${db.maxIdleTime}"></property> <!--連接池中保留的最大連接數。Default: 15 --> <property name="maxPoolSize" value="${db.maxPoolSize}"></property> <property name="minPoolSize" value="${db.minPoolSize}"></property> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 --> <property name="acquireIncrement" value="${db.acquireIncrement}"></property> <!--兩次連接中間隔時間,單位毫秒。Default: 1000 --> <property name="acquireRetryDelay" value="${db.acquireRetryDelay}"></property> <!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 --> <property name="acquireRetryAttempts" value="${db.acquireRetryAttempts}"></property> <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試 獲取連接失敗后該數據源將申明已斷開並永久關閉。Default: false --> <property name="breakAfterAcquireFailure" value="${db.breakAfterAcquireFailure}"></property> </bean> <!-- ================================事務相關控制================================================= --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0DataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="c3p0DataSource" /> <property name="configLocation" value="MyBatis-Configuration.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.gnc.mapper.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> </beans>
(2)更改UserServiceImpl.java實現類:
package com.gnc.serviceImpl; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.gnc.dao.UserDao; import com.gnc.model.User; import com.gnc.service.UserService; @Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Transactional(propagation = Propagation.SUPPORTS) public int countAll() { return this.userDao.countAll(); } @Transactional(propagation = Propagation.REQUIRED , readOnly = false) @Override public void insertUser(User user) { this.userDao.insertUser(user); throw new RuntimeException("Error"); } @Transactional(propagation = Propagation.REQUIRED , readOnly = false) @Override public void update_insert(Map map, User user) { this.userDao.updateUser(map); this.userDao.insertUser(user); throw new RuntimeException("Error"); } }
2.注解式事務管理相對聲明式事務管理顯得更加靈活,我們只需要在需要進行事務管理的方法或者類前加上@Transactional+相關控制參數即可,部分常用參數如下表:
參 數 名 稱 |
功 能 描 述 |
rollbackForClassName |
該屬性用於設置需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如: 指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException") 指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor |
該屬性用於設置不需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如: 指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName |
該屬性用於設置不需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如: 指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException") 指定多個異常類名稱: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation |
該屬性用於設置事務的傳播行為,具體取值可參考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation |
該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務並發的情況,通常使用數據庫的默認隔離級別即可,基本不需要進行設置 |
timeout |
該屬性用於設置事務的超時秒數,默認值為-1表示永不超時 |
readOnly |
該屬性用於設置當前事務是否為只讀事務,設置為true表示只讀,false則表示可讀寫,默認值為false。例如:@Transactional(readOnly=true) |
rollbackFor |
該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如: 指定單一異常類:@Transactional(rollbackFor=RuntimeException.class) 指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
需要注意的幾點:
1 @Transactional 只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.
2用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException("注釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到需要捕獲的例外(throw new Exception("注釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需我們指定方式來讓事務回滾 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .如果讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾
public void methodName() {
throw new Exception("注釋");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw new RuntimeException("注釋");)會回滾
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注釋");
}
3、@Transactional 注解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 注解的出現不足於開啟事務行為,它僅僅 是一種元數據,能夠被可以識別 @Transactional 注解和上述的配置適當的具有事務行為的beans所使用。上面的例子中,其實正是 <tx:annotation-driven/>元素的出現 開啟 了事務行為。
4、Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional 注解,但是這將只能當你設置了基於接口的代理時它才生效。因為注解是 不能繼承 的,這就意味着如果你正在使用基於類的代理時,那么事務的設置將不能被基於類的代理所識別,而且對象也將不會被事務代理所包裝(將被確認為嚴重的)。因 此,請接受Spring團隊的建議並且在具體的類上使用 @Transactional 注解。