Spring 事务的理解

今天我们来说说Spring的事务,那我们先从数据库说起,请看博主细细道来。

一、数据库事务隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。

√: 可能出现    ×: 不会出现

 注意:我们讨论隔离级别的场景,主要是在多个事务并发 的情况下,因此,接下来的讲解都围绕事务并发。

 注:MySQL的默认隔离级别就是Repeatable read。

 Serializable 是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。

二、脏读、幻读、不可重复读

我用通俗的话来说明:

  1. 脏读 : 读取到了修改前的错误数据。
  2. 不可重复度 : 多次读取的数据不一致。
  3. 幻读 : 突然改动,多读取了一条,就像产生了环境。

三、传播行为

  1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

四、如何配置Spring的事务管理

(1)、在Spring配置文件中使用AOP的方式实现事务的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
 
<!-- 定义通知 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
  <!--配置事务传播性,隔离级别以及超时回滚等问题 -->
  <tx:attributes>
    <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"   timeout="1"/>
    <tx:method name="del*" propagation="REQUIRED" />
    <tx:method name="update*" propagation="REQUIRED" />
    <tx:method name="add*" propagation="REQUIRED" />
    <tx:method name="*" rollback-for="Exception" />
  </tx:attributes>
</tx:advice>
 
<aop:config>
  <!--配置事务切点,将TestAdvice通知切入到com.website.service子目录下的所有方法 -->
  <aop:pointcut id="services" expression="execution(* com.website.service.*.*(..))" />
  <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>

(2)、注解式事务:

<1>、首先在Spring配置文件中设置事务管理器,定义通知tx:annotation-driven

1
2
3
4
5
6
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
<!--使用事务注解, 注解方式配置事务-->
<tx:annotation-driven  transaction-manager="transactionManager" />

 <2>、代码中的具体的注解以及事务的传播性、隔离级别一般在service 层中配置下面

1
2
3
4
5
6
7
8
9
10
11
12
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT)
    public void saveUser(Map<String, String> map) throws Exception {
         System.out.println("方法开始");
        for (int i = 0; i < 500000; i++) {
                System.out.println("*");
            }
         System.out.println("进入保存");
         userDao.saveUser(map);
         System.out.println("退出保存");
    }
 
@Transactional 一般是放在public方法上面,也可以放在类上面,方法和类同时有@Transactional 注解时,方法上面的注解优先级高。

 五、Spring AOP是怎样来管理事务的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1、配置文件里面用aop的方式切入事务到service的指定方法,每个service方法都被事务管理。事务的隔离级别是READ_COMMITTED(只禁止脏数据),事务的传播级别是PROPAGATION_REQUIRED(遇到新的事务,会将其加入到当前事务,合并成一个事务)
 2、这个时候需要实现一个功能,先插入数据,再查询数据,有三种写法:
     <1>、action层里面调用两个server方法:
        @Action(value="getList")
        public void getList(){
            this.Service.add();  //添加方法
            this.Service.getList(); //查询方法
        }
    分析:因为每个server是一个独立的事务,这里的action里面调用方法是按顺序执行的,
    所以是先执行添加事务并且提交事务后再执行查询事务,结果是可以查到刚添加的数据
 
 
<2>、action层里面调用一个server方法,这个server方法里面调用添加和查询server方法
    @Action(value="getList")
    public void getList(){
        this.Service.serviceFun();
    }
     
    server方法层:
    public void serviceFun(){
        this.add();
        this.getList();
    }
 
分析:因为事务的传播级别是PROPAGATION_REQUIRED(遇到新的事务,会将其加入到当前事务,合并成一个事务),所以this.add()和this.getList();是在一个事务内执行的,不存在什么添加事务提交不提交,都在一个隔离墙内,所以结果是可以查到刚添加的数据。
 
<3>、如果添加删除操作都在一个servier里面之间调用dao层方法,本来就是一个事务,那就更加可以查到刚添加的数据了。
 
以上是我对于spring Aop切入事务后,为什么我们不用考虑事务管理是原因的理解,因为spring都帮我们搞定了。

最后,最后,最后,重要的事情说三遍,以上是为了讲解事务的过程,所以使用了@Transactional

关于SpringBoot中的注解事务配置,网上一搜基本都是用@Transactional,那给每个类或方法配要给人累死了。

非常不推荐。所以想上面xml一样全局配置怎么办?同样是两步:

第一步:配置事务类

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.test.aop;
  
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
  
import javax.sql.DataSource;
  
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
  
import com.test.domain.DqeProfileDefine;
  
@Configuration
public class TxAnoConfig {
  
   
     
    @Autowired
    private DataSource dataSource;
     
    @Bean("txManager")
    public DataSourceTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource);
    }
  
    /*事务拦截器*/
    @Bean("txAdvice")
    public  TransactionInterceptor txAdvice(DataSourceTransactionManager txManager){
          
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
          /*只读事务,不做更新操作*/
         RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
         readOnlyTx.setReadOnly(true);
         readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
         /*当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务*/
         //RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
         //requiredTx.setRollbackRules(
         //    Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
         //requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
         RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED,
             Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
         requiredTx.setTimeout(5);
         Map<String, TransactionAttribute> txMap = new HashMap<>();
         txMap.put("add*", requiredTx);
         txMap.put("save*", requiredTx);
         txMap.put("insert*", requiredTx);
         txMap.put("update*", requiredTx);
         txMap.put("delete*", requiredTx);
         txMap.put("get*", readOnlyTx);
         txMap.put("query*", readOnlyTx);
         source.setNameMap( txMap );
        return new TransactionInterceptor(txManager ,source) ;
    }
  
    /**切面拦截规则 参数会自动从容器中注入*/
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(TransactionInterceptor txAdvice){
        DefaultPointcutAdvisor pointcutAdvisor = new DefaultPointcutAdvisor();
        pointcutAdvisor.setAdvice(txAdvice);
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution (* com.test.service.*.*(..))");
        pointcutAdvisor.setPointcut(pointcut);
        return pointcutAdvisor;
    }
  
  
}

 

 第二步:在pom.xml中添加依赖

1
2
3
4
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

如果你不嫌麻烦用@Aspect这个注解也行,具体百度Spring中怎么用。

两种方式比较:

我还是比较推荐xml,一眼望去,清清楚楚。功能强大,不管是哪个类中哪个方法,只要你配置了事务就能生效

注解方式,对于@controller层中的方法不生效,不知道为啥,@service层中的方法才生效。有坑。当然,对于@controller层中也要做事务,可以用@Transactional,不过一般人不需要controller开启事务吧。

 

posted @   47号Gamer丶  阅读(159)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示