第六章:Springmvc事务管理和配置文件xml详解和七大事务和注解@Transactional

本文深入解析事务的概念及其在数据库中的作用,重点介绍了ACID特性、事务的隔离级别、Spring中的事务配置方法,并通过实例详细说明了Spring七大事务传播行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务的定义

1.一个事务单元中包含多个数据库表的操作,可能有增、删、改等操作,有一个sql语句执行失败,整个事务都执⾏失败。
作用:执行增删改时,保持数据的一致、安全,不会像无事务那样出错了仍然提交

把将要执行的命令步骤给包裹在事务中,使之随时能检测命令的运行过程的异常,
及时止血,防止半途而废,事务是逻辑上的一组操作,要么都执行,要么都不执行。

事务开启后

开启事务后,执⾏的语句均属于当前事务,成功再执⾏ COMIIT,失 败要进⾏ ROLLBACK
spring事务也是通过动态代理来实现的,spring事务也是借鉴了AOP的思想

事务数据库执行原理

数据库会为每⼀个客户端都维护⼀个空间独⽴的缓存区(回滚段),⼀个事务中所有的增删改语句的执⾏结果都会缓存在回滚段中,只有当事务中所有SQL 语句 均正常结束(commit),才会将回滚段中的数据同步到数据库。否则⽆论因为 哪种原因失败,整个事务将回滚 (rollback)。

需要注意

事务能否生效与数据库引擎是否支持事务是关键。

绕不开的ACID特性

1.原子性:事务要么全执行,要么全不执行

原子性的特点:
1、针对单事务的控制,命令执行的过程中不会被加塞和干扰
2、针对多个insert,update,delete操作
典型场景,一个方法中包含多个insert,update,delete操作通过添加事务保证原子性,要么全部成功,要么全部失败

2.一致性:事务执行前后,数据完整性一致

事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的
在成功提交或失败回滚事务之后以及正在进行的事务之间,数据库始终保持不变的状态,
如果正在多个表之间更新相关数据,那么查询将看到所有旧值或所有新值,而不会一部分是新值,一部分是旧值。

3.隔离性:一个事务的执行不能被其他事务干扰。

隔离性的特点:
事务处理过程中的中间状态应该对外部不可见,换句话说,
事务在进行过程中是隔离的,事务之间不能互相干扰,不能访问到彼此未提交的数据。 
即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
1、针对多事务间数据可见性的控制。
2、控制加锁的粒度和加锁、释放锁的时机,提高事务的并发能力。

4.持久性:指一个事务一旦提交,对数据库中数据的改变是永久性,接下来的其他操作或故障不应该对其有任何影响。

何为脏读

脏读就是读到其他事务没有提交的数据,只要隔离级别不是读未提交(Read Uncommitted)就不会出现。
举例:
A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致

何为回滚

即还原到事务开始前的状态,就像这个事务从来没有执行过一样

什么情况下不需要添加事务?

对于多查询的不进行添加事务,当进行大量的增加、删除、修改表的数据进行添加事务处理。

两种引擎比较

  1. MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
  2. InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,为的是提高多用户并发操作的性能。

不是所有引擎都支持事务的处理

比如常用的 MySQL 数据库默认使用支持事务的 InnoDB引擎。但是,如果把数据库引擎变为 MyISAM,那么程序也就不再支持事务了!
大尺寸的数据集趋向于选择InnoDB引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。

分布式事务的应用场景

1、支付
2、在线下单
3.金融系统中的银行卡充值(银行系统和金融系统)、
4.教育系统中下单选课业务(订单系统和选课系统)

事务的配置

Spring的配置事务方式:声明式事务处理、注解方式的声明式事务管理

两种方式
1.通过注解的形式
2.通过xml的方式

举例:通过xml 方式配置,
创建文件SpringContext-transaction.xml

(1).在Spring核心配置文件中引入相关约束:
<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop
 		http://www.springframework.org/schema/aop/spring-aop.xsd
 		http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

(2).配置事务管理器–就是对Bean类注入属性值后的类进行事务管理
id为dataSource_ID1的BasicDataSource类配置文章链接

<!--配置事务管理器-->
 <bean id="789——transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
//ref=" " 其数据源已经完成了class="org.apache.commons.dbcp.BasicDataSource"的配置 --仔细去看就知道了
                <property name="dataSource" ref="dataSource_ID1"/>
        </bean>

(3).配置事务通知
注意:<tx:method name="以XXX开头的方法名有效*", *代表所有

<!-- 配置事务通知-->
        <tx:advice id="Advice" transaction-manager="789——transactionManager">
<!--                配置事务属性,即哪些方法要执行事务-->
                <tx:attributes>
					 <!--自定义所有insert开头的方法配置事务,以下同理 -->
                        <tx:method name="insert*" propagation="REQUIRED"/> 
					 <!--自定义所有update开头的方法配置事务,以下同理 -->
                        <tx:method name="update*" propagation="REQUIRED"/>
                        <tx:method name="delete*" propagation="REQUIRED"/>
                </tx:attributes>
        </tx:advice>

(4).配置事务切面

<!--配置事务切面-->
<aop:config>
	<!--要执行的方法在哪个包,意思为:com.wanshi.service下的所有包里面的包含任意参数的所有方法-->
	<aop:pointcut id="AdviceAop" expression="execution(public * com.xxx.service.*.*(..))"/> 
	<!-- 配置为AdviceAop执行哪个事务通知 -->
	<aop:advisor advice-ref="Advice" pointcut-ref="AdviceAop"/> 
</aop:config>

事务配置的总结

1.事务配置为事务配置管理即为DataSourceTransactionManager类中dataSource属性赋值
2.配置事务通知,通知指的是动作,自定义哪些字符串开头的命名方法添加事务动作。
3.将这些事务动作放在哪些位置或者哪个点上?即配置pointcut,像交警一样设卡查哨
这里边的expression中execution(public * com.xxx.service..(…))表达式
4.配置事务切面即将配置好的事务通知和事务的切点交给事务管理。

关于切入点表达式

主要关注 返回方法、哪个类、类中哪个方法、方法的对应参数限制
execution(* com.test1.ce.epet.dao.mpl.*.*(..)) 
这是com.test1.ce.epet.dao.impl 包下所有的类的所有方法。
配置事务的方法必须是 public
第一个 * 代表所有的返回值类型
第二个 *代表所有的类
第三个 * 代表类中所有方法 
最后一个…代表所有的参数。

Spring七大事务传播行为

1、propagation_required:支持当前事务,无事务、另起新事物
2、propagation_required-new:新建事务,若有旧事务,挂起。
3、propagation_supports:支持雪前事雾,无事务,以非事务执行
4、propagation_mandatory:以事务方式执行,无事务,抛异常
5、propagation_not_supported:不支持事务,如有事务,挂起
6、propagation_never:以非事务执行,有事务,抛异常
7、propagation_nested:内切事务

1、propagation_required

加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务。
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED,那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务。
这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。
而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。
即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

2.propagation_required-new

比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为ROPAGATION_REQUIRES_NEw。
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,
ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。
他与PROPAGATION_REQUIRED的事务区别在于事务的回滚程度。
因为ServiceB.methodB是新起一个事务,那么就是存在>两个不同的事务。
如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。
如果ServiceB.methodB失败回滚,抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

3.propagation_supports

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行。

4.propagation_mandatory

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。

5.propagation_not_supported

当前不支持事务。
比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED。
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而ServiceB.methodB以非事务的状态运行完,再继续ServiceA.methodA的事务。

6.propagation_never

不能在事务中运行。
假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,而ServiceB.methodB的事务级别是PROPAGATION_NEVER,
那么ServiceB.methodB就要抛出异常了。

7、propagation_nested:内切事务

没有在EJB中定义
理解Nested的关键是savepoint
他与PROPAGATION_REQUIRES_NEW的区别是: PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立;而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。

外围方法开启事务的情况下**Propagation.NESTED**修饰的内部方法属于外部事务的子事务,
外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。
举例
@Transactional
@Override
public void transaction_nested_nested_exception_try(){
    User1 user1=new User1();
    user1.setName("张三");
    user1Service.addNested(user1);
    User2 user2=new User2();
    user2.setName("李四");
    try {
        user2Service.addNestedException(user2);
    } catch (Exception e) {
        System.out.println("方法回滚");
    }
}
ServiceA {
“事务漓性配置为PROPAGATION_REQUIRED"
void methodA(){
	try {
iisavepoint
ServiceB.methodB();!PROPAGATION_NESTED 级别
		}
catch (SomeException)  {
		//执行其他业务
						}
			}
}
ServiceC.methodC();
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,
比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。

使用注解的方式声明事务并且加深理解Spring七大事务

使用注解的方式声明事务并且加深理解Spring七大事务

PROPAGATION_REQUIRED

支持当前事务,如果不存在则创建一个新事务。最常用的选择
场景一:service层方法内部开启事务

@Service
public class test1ServiceImpl implements test1Service {
    //省略其他...
    @Override
    #方法内部开启事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(test1 user){
        test1Mapper.update(user);
    }
}
@Service
public class test2ServiceImpl implements test2Service {
    //省略其他...
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(test2 user){
        test2Mapper.update(user);
    }
    @Override
    #方法内部开启事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequiredException(test2 user){
        test2Mapper.update(user);
        throw new Exception();
    }
}

结论:在外围方法未开启事务的情况下,PROPAGATION_REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰.

============================================

场景2:外部开启事务
开发中使用较高的场景(默认情况)

   @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_required(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addRequired(user2);

        throw new RuntimeException();
    }

结论:外围开启事务,内部方法出现一个异常均回滚。原因是外围开启事务,PROPAGATION_REQUIRED修饰的所有内部方法会加入到外围方法的事务中,内部事务和外部事务均属于同一个事务。

============================
PROPAGATION_REQUIRES_NEW

创建一个新事务,如果当前事务存在,则暂停当前事务
场景一:外部不开启事务
Service1

@Service
public class test1ServiceImpl implements test1Service {
    //省略其他...
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(test1 user){
        test1Mapper.insert(user);
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired(test1 user){
        test1Mapper.insert(user);
    }
}

Service2

@Service
public class User2ServiceImpl implements User2Service {
    //省略其他...
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(User2 user){
        user2Mapper.insert(user);
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNewException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }
}

验证方法1:外围方法没有事务,插入“张三”、“李四”方法都在自己的事务中独立运行,外围方法抛出异常回滚不会影响内部方法。
验证方法2:张三正常插入,李四无法插入,李四在自己的方法中开启新事务,但是抛出异常回滚。
场景2:外部开启事务

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception_try(){
    User1 user1=new User1();
    user1.setName("张三");
    user1Service.addRequired(user1);
    
    User2 user2=new User2();
    user2.setName("李四");
    user2Service.addRequiresNew(user2);
    User2 user3=new User2();
    user3.setName("王五");
    try {
        user2Service.addRequiresNewException(user3);
    } catch (Exception e) {
        System.out.println("回滚");
    }
}

结论:外围方法开启事务的情况下,Propagation.REQUIRES_NEW修饰的内部方法全部单独开启独立事务,不同的内部方法、外部事务间相互独立,互不干扰。

点解连接
第一章:Spring流程执行步骤
第二章:Spring核心思想和IOC和AOP依赖注入
第三章:Spring常用注解解释
第四章:Spring七大核心模块Bean、Core、Context
第五章:Spring细分一如何让mapper文件夹下的SQL语句对数据库生效,jdbc和mybatis-config.xml
第六章:Springmvc事务管理和配置文件xml详解和七大事务和注解@Transactional

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值