一、概述
Spring的事務管理分成兩類:
- 編程式事務管理(手動編寫代碼完成事務管理)
- 聲明式事務管理(不需要手動編寫代碼,需要配置)
二、准備工作
1. 創建表
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`money` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
2. 創建項目並引入Maven依賴
創建項目並引入如下Maven依賴:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- Spring 整合測試的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
結構目錄如下:

3. 編寫實體類
public class Account {
private Integer id;
private String name;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
}
}
4. 編寫Dao層
編寫接口
/**
* 銀行賬戶的相關操作 此處用了@see注釋,在實現類可以繼承接口中方法的注釋內容
*
* @author hao
* @see AccountDaoImpl
*
*/
public interface IAccountDao {
/**
* 添加賬戶
*
* @param account 要添加的賬戶
*/
public void add(Account account);
/**
* 轉出的方法
*
* @param from :轉出的賬戶,打出錢
* @param money :要轉賬金額
*/
public void out(Account from, Integer money);
/**
* 轉出的方法
*
* @param to 轉入的賬戶,收到錢
* @param money 要轉賬金額
*/
public void in(Account to, Integer money);
/**
* 通過名字查詢賬戶
* @param name 賬戶名
* @return
*/
public Account selectOneByName(String name);
}
實現類
public class AccountDaoImpl implements IAccountDao{
@Autowired
@Qualifier("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Override
public void add(Account account) {
String sql = "insert into account values(null,?,?)";
jdbcTemplate.update(sql,account.getName(),account.getMoney());
}
@Override
public Account selectOneByName(String name) {
String sql = "select * from account where name = ?";
Account account = null;
try {//這里需要捕獲結果集為空時的異常
account = jdbcTemplate.queryForObject(sql, new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getInt("money"));
return account;
}
}, name);
} catch (EmptyResultDataAccessException e) {
e.printStackTrace();
return null;
}
return account;
}
public void out(Account from, Integer money) {
String sql = "update account set money = money-? where name =? ";
jdbcTemplate.update(sql, money,from.getName());
}
public void in(Account to, Integer money) {
String sql ="update account set money = money+? where name =?";
jdbcTemplate.update(sql,money,to.getName());
}
}
5. 業務層
接口
/**
*
* @author hao
* @see AccountServiceImpl
*/
public interface IAccountService {
/**
* 向數據庫中添加用戶
*
* @param account 要添加的用戶對象
*/
public void addAccount(Account account);
/**
* 轉賬的方法
*
* @param from 轉出的賬戶
* @param to 轉入的賬戶
* @param money 轉賬金額
*/
public void transfer(Account from, Account to, Integer money);
/**
*
* @param name 需要查詢的賬戶名
* @return
*/
public Account findAccountByName(String name);
}
實現類
/**
* 沒有添加事務
* @author hao
*
*/
public class AccountServiceImpl implements IAccountService {
// 注入 accountDao 利用setter方法注入屬性
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void addAccount(Account account) {
accountDao.add(account);
}
@Override
public Account findAccountByName(String name) {
Account account = accountDao.selectOneByName(name);
return account;
}
@Override
public void transfer(Account from, Account to, Integer money) {
accountDao.out(from, money);// 轉出錢
int i= 1/0 //出現異常時
accountDao.in(to, money);// 收入錢
}
}
6. 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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置c3p0連接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 這里需要注入數據源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 此處聲明Dao層類,用注解的方式將jdbcTemplate注入到了accountDao中,也可用其他方式 -->
<bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
<!-- 此處聲明業務層類,用setter的方式注入了accountDao -->
<bean id="accountService" class="com.hao.tx.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
7. 測試
@Test
public void test02() {
System.out.println("轉賬之前....");
Account a = accountService.findAccountByName("a");
if(a != null) {
System.out.println(a);
}
Account b = accountService.findAccountByName("b");
if(b!=null) {
System.out.println(b);
}
try {//這里捕獲轉賬時出現的異常
System.out.println("開始轉賬.....");
accountService.transfer(a, b, 100);
}catch(ArithmeticException e) {
e.printStackTrace();
}finally {
System.out.println("轉賬之后....");
Account a_trans = accountService.findAccountByName("a");
if(a != null) {
System.out.println(a_trans);
}
Account b_trans = accountService.findAccountByName("b");
if(b!=null) {
System.out.println(b_trans);
}
}
}
結果分析:
運行測試類中test02 之后會發現,如果沒有事務處理的話,即使出現異常,也會改變數據庫中的數據
結果如下:


三、編程式事務
上面程序中並沒有添加事務,下面用手動編碼的方式完成事務管理,需要事務管理器去真正管理事務對象,Spring提供了事務管理的模板(工具類)
關於平台事務管理器,可以查閱【Spring】事務
1. 在業務層代碼上使用事務模板
創建業務類並使用編程式事務去進行事務管理,Spring 中提供了管理事務的模版
/**
* 編程式事務
*
* @author hao
*
*/
public class AccountServiceImpl_Program implements IAccountService {
// 注入 accountDao 利用setter方法注入屬性
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
// 注入 transactionTemplate 利用setter方法注入屬性
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void addAccount(Account account) {
accountDao.add(account);
}
@Override
public Account findAccountByName(String name) {
Account account = accountDao.selectOneByName(name);
return account;
}
@Override
public void transfer(Account from, Account to, Integer money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
accountDao.out(from, money);
int d = 1 / 0;
accountDao.in(to, money);
}
});
}
}
2. 在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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置c3p0連接池 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 在容器中聲明jdbc模版,這里需要注入數據源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 此處聲明Dao層類,用注解的方式將jdbcTemplate注入到了accountDao中,也可用其他方式 -->
<bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
<!-- (三)、在業務層注入模板類管理事務 -->
<!-- 此處聲明業務層類,用setter的方式注入了accountDao 和 管理事務的模版 -->
<bean id="accountService" class="com.hao.tx.service.impl.AccountServiceImpl_Program">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- (一)、注冊事務管理器: -->
<!-- 配置事務管理器 這里DataSourceXXX...可以用做Spring自己的或者MyBatis的事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要注入連接池,通過連接池獲得連接 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- (二)、注冊事務模板類: -->
<!-- 事務管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
</beans>
3. 測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext1.xml")
public class TxAcTest001 {
@Autowired
@Qualifier("accountService")
private IAccountService accountService;
@Test
public void test01() {
System.out.println("轉賬之前....");
Account a = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a);
}
Account b = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b);
}
try {// 這里捕獲轉賬時出現的異常
System.out.println("開始轉賬.....");
accountService.transfer(a, b, 100); //會出現異常
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("轉賬之后....");
Account a_trans = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a_trans);
}
Account b_trans = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b_trans);
}
}
}
}
結果如下:


4. 手動編碼方式缺點:
代碼量增加,代碼有侵入性.
四、聲明式事務
聲明式事務管理:(原始方式)
基於TransactionProxyFactoryBean.
1. 業務類
創建AccountServiceImpl_DeclaProxyF類,這里是為了學習時測試使用,其實無需改變業務類的任何邏輯,可以直接copy准備中的業務類。
2. 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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder
location="classpath:jdbc.properties" />
<!-- 配置c3p0連接池 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 在容器中聲明jdbc模版,這里需要注入數據源 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 此處聲明Dao層類,用注解的方式將jdbcTemplate注入到了accountDao中,也可用其他方式 -->
<bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
<!-- 此處聲明業務層類,用setter的方式注入了accountDao -->
<bean id="accountService"
class="com.hao.tx.service.impl.AccountServiceImpl_DeclaProxyF">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- (一)、注冊事務管理器: -->
<!-- 配置事務管理器 這里DataSourceXXX...可以用做Spring自己的或者MyBatis的事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要注入連接池,通過連接池獲得連接 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- (二)、創建業務層代理對象: 配置生成代理對象 -->
<bean id="accountServiceProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 目標對象 -->
<property name="target" ref="accountService" />
<!-- 注入事務管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 事務的屬性設置 -->
<property name="transactionAttributes">
<props> <!-- key值表示要進行事務管理的方法 -->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
3. 測試
注意在測試中要注入的是代理對象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class TxAcTest002 {
@Autowired
@Qualifier("accountServiceProxy") // 這里注入的代理對象
private IAccountService accountService;
@Test
public void test01() {
System.out.println("轉賬之前....");
Account a = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a);
}
Account b = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b);
}
try {// 這里捕獲轉賬時出現的異常
System.out.println("開始轉賬.....");
accountService.transfer(a, b, 100); //會出現異常
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("轉賬之后....");
Account a_trans = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a_trans);
}
Account b_trans = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b_trans);
}
}
}
}
4. 注意事項以及缺點
注意事項:配置代理對象時:prop格式:PROPAGATION , ISOLATION , readOnly, -Exception, +Exception 依次是:傳播行為、隔離級別、事務是否只讀、發生哪些異常可以回滾事務(所有的異常都回滾)、發生了哪些異常不回滾。
缺點:就是需要為每一個管理事務的類生成代理.需要為每個類都需要進行配置。
聲明值事務-基於AspectJ XML方式
1. 業務類
創建AccountServiceImpl_DeclaAspecJ類,這里是為了學習時測試使用,其實無需改變業務類的任何邏輯,可以直接copy准備中的業務類。
2. 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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置c3p0連接池 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 在容器中聲明jdbc模版,這里需要注入數據源 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 此處聲明Dao層類,用注解的方式將jdbcTemplate注入到了accountDao中,也可用其他方式 -->
<bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
<!-- 此處聲明業務層類,用setter的方式注入了accountDao -->
<bean id="accountService"
class="com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJ">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- (一)、注冊事務管理器: -->
<!-- 配置事務管理器 這里DataSourceXXX...可以用做Spring自己的或者MyBatis的事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要注入連接池,通過連接池獲得連接 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- (二)、定義增強(事務管理) -->
<!-- 定義一個增強 -->
<tx:advice id="txAdvice"
transaction-manager="transactionManager">
<!-- 增強(事務)的屬性的配置 -->
<tx:attributes>
<!-- isolation:DEFAULT ,事務的隔離級別。
propagation:事務的傳播行為.
read-only:false,不是只讀
timeout:-1
no-rollback-for:發生哪些異常不回滾
rollback-for:發生哪些異常回滾事務
-->
<tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- (三)、定義aop的配置(切點和通知的組合) -->
<!-- aop配置定義切面和切點的信息 -->
<aop:config>
<!-- 定義切點:哪些類的哪些方法應用增強 -->
<aop:pointcut
expression="execution(* com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJ+.*(..))"
id="mypointcut" />
<!-- 定義切面: -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut" />
</aop:config>
</beans>
3. 測試
這里無需注入代理對象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class TxAcTest003 {
@Autowired
@Qualifier("accountService") // 注入Service對象,不需要注入代理對象(生成這個類的時候,已經是代理對象.)
private IAccountService accountService;
@Test
public void test01() {
System.out.println("轉賬之前....");
Account a = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a);
}
Account b = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b);
}
try {// 這里捕獲轉賬時出現的異常
System.out.println("開始轉賬.....");
accountService.transfer(a, b, 100); //會出現異常
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("轉賬之后....");
Account a_trans = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a_trans);
}
Account b_trans = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b_trans);
}
}
}
}
聲明值事務-基於AspectJ 注解方式
1. 業務類
- 在Service上使用注解@Transactional,此注解可以標注在方法上,還可以標注在類上:
- 注解中有屬性值:
- isolation
- propagation
- readOnly
...
/**
* 聲明式事務-基於AspectJ 注解方式
*
* @author hao
*
*/
public class AccountServiceImpl_DeclaAspecJAnno implements IAccountService {
// 注入 accountDao 利用setter方法注入屬性
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void addAccount(Account account) {
accountDao.add(account);
}
@Override
public Account findAccountByName(String name) {
Account account = accountDao.selectOneByName(name);
return account;
}
@Override
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)
public void transfer(Account from, Account to, Integer money) {
accountDao.out(from, money);// 轉出錢
int i =1/0; // 中間出現異常
accountDao.in(to, money);// 收入錢
}
}
2. 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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置c3p0連接池 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 在容器中聲明jdbc模版,這里需要注入數據源 -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 此處聲明Dao層類,用注解的方式將jdbcTemplate注入到了accountDao中,也可用其他方式 -->
<bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
<!-- 此處聲明業務層類,用setter的方式注入了accountDao 和 管理事務的模版 -->
<bean id="accountService"
class="com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJAnno">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- (一)、注冊事務管理器: -->
<!-- 配置事務管理器 這里DataSourceXXX...可以用做Spring自己的或者MyBatis的事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要注入連接池,通過連接池獲得連接 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- (二)、開啟注解的事務管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3. 測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext4.xml")
public class TxAcTest004 {
@Autowired
@Qualifier("accountService") // 注入Service對象,不需要注入代理對象(生成這個類的時候,已經是代理對象.)
private IAccountService accountService;
@Test
public void test01() {
System.out.println("轉賬之前....");
Account a = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a);
}
Account b = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b);
}
try {// 這里捕獲轉賬時出現的異常
System.out.println("開始轉賬.....");
accountService.transfer(a, b, 100); //會出現異常
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("轉賬之后....");
Account a_trans = accountService.findAccountByName("txa");
if (a != null) {
System.out.println(a_trans);
}
Account b_trans = accountService.findAccountByName("txb");
if (b != null) {
System.out.println(b_trans);
}
}
}
}