基于Annotation方式的声明式事务
Spring的声明式事务管理还可以通过Annotation(注解)的方式来实现。这种方式的使用非常简单,开发者只需做两件事情:
① 在Spring容器中注册事务注解驱动,其代码如下:
<tx:annotation-driven transaction-manager="transactionManager"/>
② 在需要使用事务的Spring Bean类或者Bean类的方法上添加注解@Transactional。如果将注解添加在Bean类上,则表示事务的设置对整个Bean类的所有方法都起作用;如果将注解添加在Bean类中的某个方法上,则表示事务的设置只对该方法有效。
使用@Transactional注解时,可以通过其参数配置事务详情。@Transactional注解可配置的参数信息如表1所示。
表1 @Transactional注解的参数及其描述
参数名称 | 描述 |
---|---|
value | 用于指定需要使用的事务管理器,默认为"",其别名为transactionManager。 |
transactionManager | 指定事务的限定符值,可用于确定目标事务管理器,匹配特定的限定值(或者Bean的name值),默认为"",其别名为value。 |
isolation | 用于指定事务的隔离级别,默认为Isolation.DEFAULT(即底层事务的隔离级别)。 |
noRollbackFor | 用于指定遇到特定异常时强制不回滚事务。 |
noRollbackForClassName | 用于指定遇到特定的多个异常时强制不回滚事务。其属性值可以指定多个异常类名。 |
propagation | 用于指定事务的传播行为,默认为Propagation.REQUIRED。 |
read-only | 用于指定事务是否只读,默认为false。 |
rollbackFor | 用于指定遇到特定异常时强制回滚事务。 |
rollbackForClassName | 用于指定遇到特定的多个异常时强制回滚事务。其属性值可以指定多个异常类名。 |
timeout | 用于指定事务的超时时长,默认为TransactionDefinition.TIMEOUT_DEFAULT(即底层事务系统的默认时间)。 |
从表1可以看出,@Transactional注解与<tx:method>元素中的事务属性基本是对应的,并且其含义也基本相似。
为了让读者更加清楚的掌握@Transactional注解的使用,接下来对上一小节的案例进行修改,以Annotation方式来实现项目中的事务管理,具体实现步骤如下。
(1)在src目录下,创建一个Spring配置文件applicationContext-annotation.xml,在该文件中声明事务管理器等配置信息,如文件1所示。
文件1 applicationContext-annotation.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:tx="http://www.springframework.org/schema/tx"
6 xmlns:context="http://www.springframework.org/schema/context"
7 xsi:schemaLocation="http://www.springframework.org/schema/beans
8 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
9 http://www.springframework.org/schema/tx
10 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11 http://www.springframework.org/schema/context
12 http://www.springframework.org/schema/context/spring-context-4.3.xsd
13 http://www.springframework.org/schema/aop
14 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15 <!-- 1.配置数据源 -->
16 <bean id="dataSource"
17 class="org.springframework.jdbc.datasource.DriverManagerDataSource">
18 <!--数据库驱动 -->
19 <property name="driverClassName" value="com.mysql.jdbc.Driver" />
20 <!--连接数据库的url -->
21 <property name="url" value="jdbc:mysql://localhost/spring" />
22 <!--连接数据库的用户名 -->
23 <property name="username" value="root" />
24 <!--连接数据库的密码 -->
25 <property name="password" value="root" />
26 </bean>
27 <!-- 2.配置JDBC模板 -->
28 <bean id="jdbcTemplate"
29 class="org.springframework.jdbc.core.JdbcTemplate">
30 <!-- 默认必须使用数据源 -->
31 <property name="dataSource" ref="dataSource" />
32 </bean>
33 <!--3.定义id为accountDao的Bean -->
34 <bean id="accountDao" class="com.itheima.jdbc.AccountDaoImpl">
35 <!-- 将jdbcTemplate注入到AccountDao实例中 -->
36 <property name="jdbcTemplate" ref="jdbcTemplate" />
37 </bean>
38 <!-- 4.事务管理器,依赖于数据源 -->
39 <bean id="transactionManager" class=
40 "org.springframework.jdbc.datasource.DataSourceTransactionManager">
41 <property name="dataSource" ref="dataSource" />
42 </bean>
43 <!-- 5.注册事务管理器驱动 -->
44 <tx:annotation-driven transaction-manager="transactionManager"/>
45 </beans>
与基于XML方式的配置文件相比,文件5-3通过注册事务管理器驱动,替换了文件1中的第5步编写通知和第6步编写aop,这样大大减少了配置文件中的代码量。
需要注意的是,如果案例中使用了注解式开发,则需要在配置文件中开启注解处理器,指定扫描哪些包下的注解。这里没有开启注解处理器是因为在配置文件中已经配置了AccountDaoImpl类的Bean,而@Transactional注解就配置在该Bean类中,所以可以直接生效。
(2)在AccountDaoImpl类的transfer()方法上添加事务注解,添加后的代码如下所示。
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT, readOnly = false)
public void transfer(String outUser, String inUser, Double money) {
// 收款时,收款用户的余额=现有余额+所汇金额
this.jdbcTemplate.update("update account set balance = balance +? "
+ "where username = ?",money, inUser);
// 模拟系统运行时的突发性问题
int i = 1/0;
// 汇款时,汇款用户的余额=现有余额-所汇金额
this.jdbcTemplate.update("update account set balance = balance-? "
+ "where username = ?",money, outUser);
}
上述方法已经添加了@Transactional注解,并且使用注解的参数配置了事务详情,各个参数之间要用英文逗号“,”进行分隔。
小提示:
在实际开发中,事务的配置信息通常是在Spring的配置文件中完成的,而在业务层类上只需使用@Transactional注解即可,不需要配置@Transactional注解的属性。
(3)在TransactionTest类中,创建测试方法annotationTest(),编辑后的代码如下所示。
@Test
public void annotationTest(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext-annotation.xml");
// 获取AccountDao实例
AccountDao accountDao =
(AccountDao)applicationContext.getBean("accountDao");
// 调用实例中的转账方法
accountDao.transfer("Jack", "Rose", 100.0);
// 输出提示信息
System.out.println("转账成功!");
}
从上述代码可以看出,与XML方式的测试方法相比,该方法只是对配置文件的名称进行了修改。程序执行后,会出现与XML方式同样的执行结果,这里就不再做重复演示,读者可自行测试。