SpringMVC声明式事务
事务并发、传播性、隔离级别(重难点)
导读:本节重点在于多线程并发环境下的事务处理、和数据库在并发环境下的表锁和行锁。
案例:在新增图书的时候,肯定需要先新增作者。
1、SpringMVC声明式事务
事务分两种:编程式事务、声明式事务
Connection conn
conn.setAutoCommit(false)
conn.commit() conn.rollback()
1.1 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
1.2 配置事务
<!--配置声明式事务-->
<!--采用数据源事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--允许事务以注解的方式进行配置-->
<tx:annotation-driven transaction-manager="transactionManager"/>
1.3 使用测试
@Transactional//此注解表示,此方法的事务交给Spring管理
public void save(TbBook book){
//新增作者新增,并且需要返回主键
int authorId = authorService.save(book.getAuthor());
if (1==1){
throw new NullPointerException("故意的");
}
book.setAuthorId(authorId);
//完成新增图书的操作
bookMapper.save(book);
}
2、事务的传播
导读:我们知道,mvc模型中一般将事务放在service层控制,那么问题是:service层方法A调用service层方法B,方法A和方法B是否在同一个事务中?
案例1:方法A和方法B上都有事务注解,方法A调用方法B,此时方法B中出现异常,是否会导致方法A回滚?
答案:会回滚
分析:方法A和方法B在同一个事务中,证明方法A的事务会传递给方法B,这就是Spring声明式事务的传播性。
案例2:方法A调用方法B,方法B出现异常,不要影响到方法A的成功事务执行。
2.1 举例
当事务A中包含了事务B,如果事务B出现了回滚,那么事务A也会回滚,此时事务A和事务B成为一个事务整体。
如何实现事务B的回滚不影响事务A呢? NESTED
方法B:在方法B的声明式事务中声明事务的传播特性为NESTED
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
public void addOrderInfo(List<TOrderinfo> orderinfos) throws Exception {
if(orderinfos.isEmpty() || orderinfos==null) {
throw new NullPointerException();
}
orderinfoMapper.insertOrderInfos(orderinfos);
}
2.2 Spring中的事务传播性(声明式事务)
涉及到事务的作用域。 session和事务间的关系。具体的事务传播特性的属性值或事务的传播行为有如下几种:
注意 PROPAGATION_REQUIRED和PROPAGATTION_NESTED
1)PROPAGATION_REQUIRED:支持当前事务,没有事务就新建一个。(默认)
比如:方法A调用方法B,方法A有事务,方法B会延用方法A的事务,而不会重新创建。如果方法A没有事务,方法B有事务,方法B会新建事务。
2)PROPAGATION_SUPPORTS:支持当前事务,如果没有事务,不新建事务。
比如:方法A调用方法B,方法A有事务,方法B会延用方法A的事务,而不会重新创建。如果方法A不支持事务,方法B有事务,方法B不会新建事务。
3)PROPAGATION_MANDATORY:支持当前事务,没有事务会抛异常
比如:方法A调用方法B,方法A有事务,则方法B也有事务。如果方法A没有事务,方法B需要事务就会抛出异常。
4)PROPAGATION_REQUIRES_NEW:使用新建事务,如果当前存在事务,把当前事务挂起
比如:方法A调用方法B,不管方法A是否支持事务,方法B都会新建事务(挂起方法A的事务)。
5)PROPAGATION_NOT_SUPPORTED:不支持事务操作,有事务则挂起
比如:方法A调用方法B,不论方法A是否有事务,方法B都不支持事务。
6)PROPAGATION_NEVER:不支持事务,有事务则挂起
比如:方法A调用方法B,不论方法A是否有事务,方法B都不支持事务。
7)PROPAGATTION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作
同 PROPAGATION_REQUIRED 操作
注意:
内层事务的异常可能会影响到外层事务。即使内层开启的新事务。原因:内层方法出现异常,会导致被挂起的异常无法正常执行完成。
事务传播:
1、默认情况下,spring方法中事务的传播特性为:如果方法的上一级方法存在事务,则方法会沿用上一级方法的事务。
PROPAGATION_REQUIRES_NEW 开启新的session
3、事务隔离级别
导读:事务的四大特性中的隔离性的实现会带来一定的性能消耗~ 要实现事务的隔离性,就需要避免线程同步操作同一条数据。事务的隔离级别问题分类如下:
1、脏读(事务a读取了事务b没有提交的数据)
2、不可重复读(事务a读取了数据后,事务b对此数据进行了修改,事务a再次读取发现不一致)
3、幻读(事务a读取一个范围的数据后,事务b又修改了一条数据进来,导致事务a再次读取时发现不一样,多出一行数据,好像出现幻觉一样)。
隔离级别是事务四大特性之一的隔离性的级别。隔离也分不同级别的隔离:
读未提交(read uncommitted):没有隔离
读已提交(read committed)
可重复度(repeatable read):默认
串行化(serializable)
事务隔离级别的实现方式是加锁,隔离级别越高,锁影响的范围越大。效率越低。
3.1、脏读(最低隔离级别)
修改数据库的事务隔离级别:
set session transaction isolation level read uncommitted;
//session表示当前会话(请求),transaction就表示事务,isolation表示隔离,level表示级别
事务隔离级别的分类:
read uncommitted、read committed、repeatable read(默认)、serializable(串行)
脏读演示
解决脏读:配置事务隔离级别为read committed。
脏读现象:一个事务读取了另一个事务没有提交的内容~~
1、开启两个客户端,都开启事务
2、一个客户端用来读数据,一个客户端用来写数据(但是不提交事务,之后做事务回滚)。
3、写操作进行事务回滚
避免脏读
数据库默认是避免脏读的~
select @@tx_isolation;
##设置数据的事务的隔离级别
##read uncommitted能读取其他事务没有提交的内容,最低的隔离级别
set session transaction isolation level repeatable read;
start transaction;
select * from tb_account where account_id=1 for update;
update tb_account set account_money=200 where account_id=1;
commit;
insert into tb_account(account_money,account_user_id) values(100,2);
rollback;
select @@tx_isolation;
select * from tb_account;
start transaction;
3.2 锁
数据库引擎常见分2种:myisam和innodb。
数据库引擎:是一种数据操作的机制。
目前数据库默认存储引擎:InnoDB。
区别:
InnoDB:支持事务、行锁 MySql8.0
MyIsam:不支持事务、表锁
InnoDB
行锁(共享锁(读锁)和排它锁(写锁)):
SELECT ... LOCK IN SHARE MODE //共享锁、乐观锁、读锁
特点:对读操作共享,写操作排队
SELECT ... FOR UPDATE; //排他锁
MyIsam
lock table tb_user read
unlock tables;