一、什么是Spring事务
事务:指作为单个逻辑工作单元执行的一系列操作,要么完全执行,要么都不执行。简单的说,事务就是并发控制单位,是用户定义的一个操作序列。
而Spring事务也同样满足ACID属性:
A:原子性(Atomicity)
事务中的操作要么都不做,要么就全做。
C:一致性(Consistency)
事务执行的结果必须是从数据库从一个一致性状态转换到另一个一致性状态。
I:隔离性(Isolation)
一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
D:持久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的
与数据库事务不同是,Spring框架事务的逻辑单元由Java代码构成,保证要么事务内的Java代码全都执行,要么全都不执行。
二、配置事务(声明式)
1、导入jar包
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"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.jd"></context:component-scan>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"></bean>
<!-- 启用事务注解 -->
<tx:annotation-driven proxy-target-class="true"/>
</beans>
注意:上面代码块最后一行配置文件中proxy-target-class配置属性为true代表使用CGLib代理,而不是使用JDK代理(详可参考博客:详述JDK代理与CGLib代理区别)
3、在需要添加事务的方法上添加@Transactional注解
@Service
public class CouponService implements ICouponService {
@Autowired
private IBookDao bookDao;
@Autowired
private IMoneyDao moneyDao;
@Autowired
private ICouponDao couponDao;
//购买
@Override
@Transactional
public boolean insert(String userId,String bookId, int count){
//判断书籍库存是否足够
if(bookDao.enough(bookId, count)) {
//书籍表库存递减
bookDao.update(bookId, count);
}
//判断钱包余额是否足够
double price = bookDao.getPrice(bookId);
double total = price*count;
if(moneyDao.enough(userId, total)) {
//订单表添加数据
Coupon coupon = new Coupon();
coupon.setId(UUID.randomUUID().toString());
coupon.setUserId(userId);
coupon.setBookId(bookId);
coupon.setTotal(total);
couponDao.insert(coupon);
//钱包表递减
moneyDao.update(userId, total);
}
return true;
}
}
在上面实例中,在insert()方法上添加了@Transactional注解成为一个事务单元,只有该方法内的全部代码执行成功,该方法才会真正执行,否则会发生回滚。
三、@Transactional常用属性
1、timeout属性:设置最大等待时长,判断事务是都有效超时则会抛出异常
@Service
public class CouponService implements ICouponService {
@Autowired
private IBookDao bookDao;
@Autowired
private IMoneyDao moneyDao;
@Autowired
private ICouponDao couponDao;
@Override
@Transactional(timeout=3)//设置最大等待时长3s
public boolean insert(String userId,String bookId, int count){
if(bookDao.enough(bookId, count)) {
bookDao.update(bookId, count);
}
try {
Thread.sleep(4000);//设置等待4s
} catch (InterruptedException e) {
e.printStackTrace();
}
double price = bookDao.getPrice(bookId);
double total = price*count;
if(moneyDao.enough(userId, total)) {
Coupon coupon = new Coupon();
coupon.setId(UUID.randomUUID().toString());
coupon.setUserId(userId);
coupon.setBookId(bookId);
coupon.setTotal(total);
couponDao.insert(coupon);
moneyDao.update(userId, total);
}
return true;
}
}
2、readOnly属性:设置事务是否为“只可读”,若为true,执行到修改数据库代码时,会发生回滚并抛出异常
@Transactional(readOnly=true)
运行结果:
3、rollbackFor属性:设置需要进行回滚的异常类数组。注意,默认发生检查时异常不进行回滚,需手动设置
@Transactional(rollbackFor= {MoneyException.class,RuntimeException.class})
运行结果:
4、propagation属性:设置事务传导机制,当出现“事务套事务”时,两层事务默认合并成一个事务。如果设置不合并,生成新的事务时,内层事务若发生回滚,则外层事务不会回滚。
@Transactional(propagation=Propagation.REQUIRES_NEW)
内层事务
外层事务