1.Spring事务管理的两种方式
Java EE应用的事务策略分为全局事务和局部事务。大多数情况下,我们都使用局部事务,所以这篇文章就不说全局事务了。
而Spring 框架为局部事务提供了两种管理方式,分别如下
(1)编程式事务管理:通过编程实现,但我们太懒了,所以一般不用。
(2)声明式事务管理:通过声明实现。
而声明式事务管理又可以通过以下两种方式实现
- 基于配置文件实现
- 基于注解的实现
2.Spring事务管理的API
Spring框架提供的事务管理抽象层主要由org.springframework.transaction包下的三个接口组成:
TransactionDefinition接口:用于描述事务的隔离级别、传播规则、超时时间、是否为只读事务等特性,可以编程设置这些特性,也可以通过XML文件或者注解配置。
TransactionStatus接口:用于描述事务的状态,事务管理器通过该接口可以获取事务的运行期状态信息,也可以通过该接口间接的回滚事务,它相比于在抛出异常时回滚事务的方式更具有可控性。
PlatformTransactionManager接口:该接口代表事务管理器,是Spring事务管理的核心接口。
针对不同的持久化技术,Spring提供了PlatformTransactionManager接口的不同实现类:
JpaTransactionManager:使用JPA持久化的事务管理器。
HibernateTransactionManager:使用Hibernate持久化的事务管理器。
DataSourceTransactionManager:使用Spring JDBC、iBatis等DataSource数据源持久化时的事务管理器。
JdoTransactionManager:使用JDO持久化的事务管理器。
JtaTransactionManager:使用全局事务的事务管理器。
3.步骤
无论是使用XML配置事务还是使用注解配置事务,都有以下几步
(1)配置数据源
(2)配置事务管理器
(3)配置事务增强
(4)配置事务增强的切面
准备工作
导jar包,这里我们使用了c3p0连接池,所以导入了c3p0的jar包
引入约束
新建数据库account
新建dao和service
public class AccountDao {
// JdbcTemplate实例
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 账户减少钱
*/
public void lessAccount() {
String sql = "update account set money = money - ? where name = ?";
jdbcTemplate.update(sql, 1000, "CodeTiger");
}
/**
* 账户增加钱
*/
public void moreAccount() {
String sql = "update account set money = money + ? where name = ?";
jdbcTemplate.update(sql, 1000, "xu");
}
}
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void accountMoney() {
// 一个账户减少钱
accountDao.lessAccount();
// 提现事务的功能
int a = 10 / 0;
System.out.println(a);
// 一个账户增加钱
accountDao.moreAccount();
}
}
基于XML配置文件的事务管理
(1)配置数据源
配置了c3p0连接池一些最基本的参数,仅用于连接。
(2)配置事务管理器
不同的持久化技术,使用的事务管理器不一样,这里我们使用JDBC的事务管理器。
(3)配置事务增强
使用
id:唯一标识。
transaction-manager:已经配置的事务管理器bean的id,如果事务管理器bean的id值为transactionManager,则可以省略此属性。
name:配置需要被事务增强的方法名,可以使用通配符*。
propagation:配置事务的传播类型。可选择REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED之一,默认为REQUIRED。
isolation:配置事务的隔离级别。可选择DEFAULT、READ_UNCOMMITED、READ_COMMITED、REPEATABLE_READ、SERIALIZABLE之一,默认为DEFAULT。
timeout:配置事务超时时间(以秒为单位),默认为-1,表示事务超时的时间由底层的事务系统决定。
read-only:配置是否为只读事务,默认为false。
rollback-for:配置需要自动回滚事务的异常类型,默认所有RuntimeException都会回滚。
no-rollback-for:配置不触发自动回滚事务的异常类型,默认所有CheckedException都不会回滚。
下面的代码配置事务增强
(4)配置事务增强的切面
这一步将事务增强与切入点关联在一起。配置ide方法与普通的切面配置相同。
事务管理也使用了AOP的原理,即把事务增强看成切面,service看成切点(当然也可以把dao看成切点,这时事务增强的方法匹配规则就得相应的进行修改)。
当然,配置文件中还少了一些东西,比如service和dao的实例化,以及获取操作数据库的实例对象。把它们也加上。
最后写一个测试方法进行测试
@Test
public void accountTest() {
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService)context.getBean("accountService");
service.accountMoney();
}
运行测试,出现除0异常,刷新数据库,账户余额没有发生变化,说明事务管理生效了。
基于注解的事务管理
(1)启用注解式事务配置
使用注解配置事务,首先需要在XML配置文档中开启事务的注解
该标签有三个属性
- transaction-manager:已经配置好的事务管理器Bean的id,如果未指定,则会查找id为transactionManager的事务管理器Bean。
- proxy-target-class:是否通过cglib创建子类代理对象。
- order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。
(2)使用@Transactional注解
业务类或业务类的方法如果需要被增强,则需要使用@Transactional注解进行配置。
@Transactional注解的参数和前面讲的
@Transactional注解可用于接口、接口的方法、类、类的public方法上,由于注解的不可继承性,通常建议在类上使用该注解。
如下,给service类加注解
// 开启事务,对该类的所有方法都生效
@Transactional
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Transactional(readOnly = true)
public void accountMoney() {
// 一个账户减少钱
accountDao.lessAccount();
// 体现事务的功能
int a = 10 / 0;
System.out.println(a);
// 一个账户增加钱
accountDao.moreAccount();
}
}
上面的代码中,给AccountService 类加了@Transactional注解,给accountMoney方法也加了@Transactional注解并设置为只读。因此最终的配置结果就是AccountService 类的所有方法都会被增强,并且accountMoney方法使用的是只读事务。(覆盖了类上的注解)