1.事务传播属性
- 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行
- 事务的传播行为可以由传播属性指定
spring定义了7中传播行为:
REQUIRED:如果有事务在运行当前的方法就在这个事务内运行。否则,就启动一个新的事务,并在自己的事务内运行
REQUIRES_NEW:当前的方法必须启动新事务,并在它的事务内运行。如果有事务在运行,那就将它挂起
SUPPORTS:如果有事务在运行,当前的方法在就这个事务内运行。否则,他可以不在事务中运行
NOT_SUPPORTE:当前的方法不应该在事务中运行,如果有运行的事务,就将它挂起
MANDATORY:当前的方法必须要运行在事务内部,如果没有正在运行的事务,则抛出异常
NEVER:当前的方法不应该在事务中运行,如果有正在运行的事务,则抛出异常
NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则,就启动一个新事务
1.1需求
一个用户需要买2本书,但是剩余的钱只可以够买其中的一本书。用户执行此方法(用余额去买这两本书)后,结果是 买了一本书?还是没买成功?
1.1.1开发
在之前的项目下com.zzc.service包创建CashierService接口和其实现类CashierServiceImpl
CashierService .java
public interface CashierService {
void checkOut(String username, List<Integer> ids);
}
CashierServiceImpl.java
@Service(value="cashierService")
public class CashierServiceImpl implements CashierService {
@Autowired
private BookShopService bookShopService;
@Transactional
@Override
public void checkOut(String username, List<Integer> ids) {
for(Integer id : ids) {
bookShopService.purchase(username, id);
}
}
}
checkOut()方法也添加了一个事务,之前purchase()也是添加了一个事务的。此时,事务是如何传播的?
测试类TxTest:
public class TxTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private CashierService cashierService = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = (BookShopDao) ctx.getBean("bookShop");
bookShopService = (BookShopService) ctx.getBean("bookShopService");
cashierService = (CashierService) ctx.getBean("cashierService");
}
@Test
public void test03() {
cashierService.checkOut("zzc", Arrays.asList(1, 2));
}
...
}
上述方法意思是:剩余的钱只够买编号1或编号2的其中一本书。执行上述方法后,结果一本书都没有买成功
表明:spring的默认事务行为是REQUIRED
checkOut()方法开启了一个事务,再调用purchase()(也开启了事务)方法,那么purchase()方法不会开启自己的事务,直接就运行在checkOut()方法中的事务。所以第一本书购买成功,第二本书购买时抛出异常,然后事务回滚到初始状态。
如何改变事务的传播行为?
给@Transactional注解中的propagation属性赋值即可
如:@Transactional(propagation=Propagation.REQUIRED)
2.隔离级别
- 当同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行,可能会出现许多意外的问题
- 并发事务所导致的问题可以分为三类:
脏读:对于两个事务T1、T2,T1读取了已经被T2更新但还没有提交的字段。之后,若T2回滚,则T1读取的内容就是临时且无效的
不可重复读:对于两个事务T1、T2,T1读取了一个字段,然后T2更新该字段。之后,T1又读取了相同的字段,值就不同了
幻读:对于两个事务T1、T2,T1从一个表中读取了一个字段,然后,T2在该表中插入了一些新的行。之后,T1再次读这个表,就会多出几行
如何改变事务的隔离级别?
给@Transactional注解中的isolation属性赋值即可
如:@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
3.回滚
- 默认情况下,spring的声明式事务对所有的运行时异常进行回滚
- 通常情况下,不进行设置,默认值即可
4.只读
- 使用readOnly指定事务是否为只读。表示这个事务只读取数据,这样可以帮助数据库引擎优化此事务
如:@Transactional(readOnly=true)
5.超时
- 使用timeout指定强制回滚之前事务可以占用的时间