SpringMVC声明式事务

事务并发、传播性、隔离级别(重难点)

导读:本节重点在于多线程并发环境下的事务处理、和数据库在并发环境下的表锁和行锁。

案例:在新增图书的时候,肯定需要先新增作者。

SpringMVC声明式事务

事务分两种:编程式事务、声明式事务

Connection conn

conn.setAutoCommit(false)

conn.commit() conn.rollback()

导包

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>

配置

<!--配置声明式事务-->
    <!--采用数据源事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--允许事务以注解的方式进行配置-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

使用

@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);

    }

一、事务的传播

导读:我们知道,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的成功事务执行。

1.1、Spring中的事务传播性(声明式事务)

涉及到事务的作用域。 session和事务间的关系。事务的传播行为有如下几种:

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

二、事务隔离级别

导读:事务的四大特性中的隔离性的实现会带来一定的性能消耗~ 要实现事务的隔离性,就需要避免线程同步操作同一条数据。事务的隔离级别问题分类如下:
1、脏读(事务a读取了事务b没有提交的数据)
2、不可重复读(事务a读取了数据后,事务b对此数据进行了修改,事务a再次读取发现不一致)
3、幻读(事务a读取一个范围的数据后,事务b又修改了一条数据进来,导致事务a再次读取时发现不一样,多出一行数据,好像出现幻觉一样)。

隔离级别是事务四大特性之一的隔离性的级别。隔离也分不同级别的隔离:

读未提交(read uncommitted):没有隔离

读已提交(read committed)

可重复度(repeatable read):默认

串行化(serializable)

事务隔离级别的实现方式是加锁,隔离级别越高,锁影响的范围越大。效率越低。

2.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;

2.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;
posted @ 2024-03-10 15:05  最怕万一见温柔  阅读(21)  评论(0编辑  收藏  举报