Spring 事务的理解
今天我们来说说Spring的事务,那我们先从数据库说起,请看博主细细道来。
一、数据库事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。
√: 可能出现 ×: 不会出现
注意:我们讨论隔离级别的场景,主要是在多个事务并发 的情况下,因此,接下来的讲解都围绕事务并发。
注:MySQL的默认隔离级别就是Repeatable read。
Serializable 是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
二、脏读、幻读、不可重复读
我用通俗的话来说明:
- 脏读 : 读取到了修改前的错误数据。
- 不可重复度 : 多次读取的数据不一致。
- 幻读 : 突然改动,多读取了一条,就像产生了环境。
三、传播行为
- PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
- PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- 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开启事务吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具