一、@Transactional 注解
progpagation——Propagation:设置事务的传播行为;isolation——Isolation:设置事务的隔离级别;timeout——int:设置事务的超时时间,事务超出指定执行时长后自动终止并回滚;noRollbackFor——Class[]:哪些异常事务可以不回滚noRollbackForClassName——String[]:(String全类名)rollbackFor——Class[]:哪些异常事务需要回滚;rollbackForClassName——String[]:(String全类名)readOnly——boolean:设置事务中的一些列的操作是否为只读;
二、超时属性
语法格式:
@Transactional(timeout = 3)//毫秒值
超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。
该属性是设置在事务强制回滚前最多可执行(等待)时间;
三、只读属性
@Transactional(readOnly = true) //默认是 false
readOnly 属性:
该属性指定当前事务中一系列的操作是否为只读,能够加快查询,不再关心事务。
四、触发事务回滚的异常
1、默认情况
异常分类:
运行时异常(非检查异常):可以不用处理,默认都回滚;
编译时异常(检查异常):要么try-catch,要么在方法上面声明 throws 默认不回滚;
2、rollbackFor 属性
rollbackFor 属性:指定遇到时必须进行回滚的异常类型,可以为多个;
rollbackForClassName 属性:String 全类名
举例:
@Transactional(rollbackFor = {FileNotFoundException.class})
@Transactional(rollbackForClassName = {"java.io.FileNotFoundException"})
3、noRollbackFor 属性
noRollbackFor 属性:指定遇到时不回滚的异常类型,可以为多个;
noRollbackForClassName 属性:String 全类名
举例:
@Transactional(noRollbackFor = {ArithmeticException.class})
@Transactional(noRollbackForClassName = {"java.lang.ArithmeticException"})
五、事务的隔离级别
1、数据库事务并发问题
假设现在有两个事务:Transaction01 和 Transaction02 并发执行。
(1)脏读
【1】Transaction01 将某条记录的 AGE 值从 20 修改为 30;【2】Transaction02 读取了 Transaction01 更新后的值:30;【3】Transaction01 回滚,AGE 的值恢复到了 20;【4】Transaction02 读取的30 就是一个无效的值;(脏数据)
(2)不可重复读
【1】Transaction01 读取了 AGE 的值为20;【2】Transaction02 将 AGE 值修改为 30;【3】Transaction01 再次读取 AGE 值为30,和第一次读取不一致;
(3)幻读
【1】Transaction01 读取了 Student 表中的一部分数据;【2】Transaction02 向 Student 表中插入了新的行;【3】Transaction03 读取了 Student 表时,多出了一些行;(出现了幻觉)
2、隔离级别
3、各个隔离级别解决并发问题的能力表
|
脏读
|
不可重复读
|
幻读
|
READ UNCOMMITTED
|
有
|
有
|
有
|
READ COMMITTED
|
无
|
有
|
有
|
REPEATABLE READ
|
无
|
无
|
有
|
SERIALIZABLE
|
无
|
无
|
无
|
4、各个数据库产品对事务隔离级别的支持程度
|
Oracle
|
MySQL
|
READ UNCOMMITTED
|
×
|
√
|
READ COMMITTED
|
√(默认)
|
√
|
REPEATABLE READ
|
×
|
√(默认)
|
SERIALIZABLE
|
√
|
√
|
5、在 Spring 中指定事务隔离级别
(1)使用注解
用 @Transaction 注解声明式地管理事务时可以在 @Transaction 的 Isolation 属性中设置隔离级别。
(2)XML 方式
在Spring 2.x事务通知中,可以在< tx:method >元素中指定隔离级别
<tx:advice id="bookShpTXAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
6、详解隔离级别
读未提交:脏读 1读已提交:不可重复读 2可重复读:幻读 4串行化:性能低、消耗大 8
7、MySQL 数据设置隔离级别
查询 MySQL 的隔离级别
select @@global.tx_isolation; //查询全局隔离级别
select @@session.tx_isolation; //查询当前会话隔离级别
select @@tx_isolation; //默认是会话隔离级别
修改MySQL隔离级别
set [session | global] transaction isolation level [READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE]
事务操作
开启事务 start transaction
提交事务 commit
回滚事务 rollback
8、并发修改同一个数据下的排队
案例:
9、ioc 容器中保存的是业务逻辑组件(有事务)的代理对象
六、事务的传播行为
1、传播行为
传播行为(事务的传播+事务的行为):如果有多个事务进行嵌套运行,子事务是否要和大事务共用一个事务;
2、Spring的7种传播行为
事务传播属性可以在 @Transaction 注解的 propagation 属性中定义。
3、测试
4、REQUIRED传播行为
事务的属性都是继承于大事务;自己的都失效
只有这一条线上任何一处出现异常,整条先全崩,回滚
将之前事务用的connection传递给这个方法使用
5、REQUIRES_NEW传播行为
表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。
REQUIRED_NEW:开一辆新车(你翻车不影响我)
不继承大事务,自己用自己的
一处事务崩,只是他崩了,跟其他没有关系,他自己回滚。
这个方法直接使用新的connection
6、详解传播行为
7、REQUIRED 事务属性来源于最外层的大事务属性。
如果是 REQUIRED :事务的属性都是继承于最外层的大事务属性,而 REQUIRED_NEW 是可以调整;
8、本类事务方法之间的调用就只是一个事务(事务失效)
举例:
@Service
public class BookService {
@Autowired
private BookDao bookDao;
/**
* 结账,哪个用户买了哪本书
* @param userName
* @param isbn
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkOut(String userName, String isbn) {
bookDao.updateStock(isbn);
int price = bookDao.getPrice(isbn);
int i = 10 / 0;
bookDao.updateBalance(userName, price);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice() {
}
@Transactional
public void mulTx() {
checkOut("Tom", "ISBN-001");
updatePrice();
}
}
在上面的 mulTx() 方法中,直接调用本类的事务方法,并且事务的传播行为都是 REQUIRED_NEW,但是运行 mulTx() 方法时,事务并没有正常运行。
注意:Spring是利用AOP来实现事务的,而且在容器中保存的代理对象,我们应该调用代理对象的事务方法,直接调用本类的事务方法,并没有代理对象进行事务控制。
上面的 mulTx() 相当于只有一个 mulTx() 一个事务方法:
BookServiceProxy.mulTx() {
checkOut("Tom", "ISBN-001");
updatePrice();
}
正确写法:
@Autowired
private BookDao bookDao; //注入的是代理对象
@Transactional
public void mulTx() {
bookService.checkOut("Tom", "ISBN-001");
bookService.updatePrice();
}
通过代理对象来调用,代理对象对原来方法做了事务控制(增强)。
本类事务方法的嵌套调用就只有一个事务。