概述
Spring针对Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事务 API,实现了一致的编程模型,而Spring的声明式事务功能更是提供了极其方便的事务配置方式,配合Spring Boot的自动配置,大多数Spring Boot项目只需要在方法上标记@Transactional
注解,即可一键开启方法的事务性配置。
但是,事务如果没有被正确出,很有可能会导致事务的失效,带来意想不到的数据不一致问题,随后就是大量的人工接入查看和修复数据,该篇主要分享Spring事务在技术上的正确使用方式,避免因为事务处理不当导致业务逻辑产生大量偶发性BUG。
在分析事务失效的常见场景之前,我们先来了解一下:事务的传播类型 和 @Transactionnal
注解的不同属性的含义。
事务的传播类型
//如果有事务, 那么加入事务, 没有的话新建一个(默认)
@Transactional(propagation=Propagation.REQUIRED)
//容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//不管是否存在事务, 都创建一个新的事务, 原来的挂起, 新的执行完毕, 继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
//必须在一个已有的事务中执行, 否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
//必须在一个没有的事务中执行, 否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
//如果其他bean调用这个方法, 在其他bean中声明事务, 那就用事务, 如果其他bean没有声明事务, 那就不用事务
@Transactional(propagation=Propagation.SUPPORTS)
isolation
该属性用于设置底层数据库的事务隔离级别,事务的隔离级别介绍:
// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读) Oracle默认
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读) MySQL默认
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)
@Transactionnal注解属性
@Transactional
注解可以作用于接口、接口方法、类以及类方法上,它可以通过不同的参数来选择什么类型Exception异常下执行回滚或者不回滚操作。

Spring事务失效的场景
1. 事务方法未被Spring管理
如果事务方法所在的类没有注册到Spring IOC容器中,也就是说,事务方法所在类并没有被Spring管理,则Spring事务会失效,举个例子🌰:
/**
* 商品业务实现层
*
* @author: austin
* @since: 2023/2/10 14:19
*/
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {
@Autowired
private ProductMapper productMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateProductStockById(Integer stockCount, Long productId) {
productMapper.updateProductStockById(stockCount, productId);
}
}
ProductServiceImpl
实现类上没有添加@Service
注解,Product的实例也就没有被加载到Spring IOC容器,此时updateProductStockById()
方法的事务就会在Spring中失效。
2. 方法使用final类型修饰
有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如:
@Service
public class OrderServiceImpl {
@Transactional
public final void cancel(OrderDTO orderDTO) {
// 取消订单
cancelOrder(orderDTO);
}
}
OrderServiceImpl
的cancel取消订单方法被final修饰符修饰,Spring事务底层使用了AOP,也就是通过JDK动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,从而无法添加事务功能。这种情况事务就会在Spring中失效。
💡Tips: 如果某个方法是static的,同样无法通过动态代理将方法声明为事务方法。
3. 非public修饰的方法
如果事务方式不是public修饰,此时Spring事务会失效,举个例子🌰:
/**
* 商品业务实现层
*
* @author: austin
* @since: 2023/2/10 14:19
*/
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {
@Autowired
private ProductMapper productMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void updateProductStockById(Integer stockCount, String productId) {
productMapper.updateProductStockById(stockCount, productId);
}
}
虽然ProductServiceImpl
添加了@Service
注解,同时updateProductStockById()
方法上添加了@Transactional(propagation = Propagation.REQUIRES_NEW)
注解,但是由于事务方法updateProductStockById()
被 private 定义为方法内私有,同样Spring事务会失效。
4. 同一个类中的方法相互调用
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
@Override
public ResponseEntity submitOrder(Order order) {
// 保存生成订单信息
long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
order.setOrderNo("ORDER_" + orderNo);
orderMapper.insert(order);
// 扣减库存
this.updateProductStockById(order.getProductId(), 1L);
return new ResponseEntity(HttpStatus.OK);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateProductStockById(Integer num, Long productId) {
productMapper.updateProductStockById(num, productId);
}
}
submitOrder()
方法和updateProductStockById()
方法都在OrderService类中,然而submitOrder()
方法没有添加事务注解,updateProductStockById()
方法虽然添加了事务注解,这种情况updateProductStockById()
会在Spring事务中失效。
5. 方法的事务传播类型不支持事务
如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务同样会在Spring中失效,举个例子:
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public ResponseEntity submitOrder(Order order) {
long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
order.setOrderNo("ORDER_" + orderNo);
orderMapper.insert(order);
// 扣减库存
this.updateProductStockById(order.getProductId(), 1L);
return new ResponseEntity(HttpStatus.OK);
}
/**
* 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateProductStockById(Integer num, Long productId) {
productMapper.updateProductStockById(num, productId);
}
}
6. 异常被内部catch,程序生吞异常
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public ResponseEntity submitOrder(Order order) {
long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));
order.setOrderNo("ORDER_" + orderNo);
orderMapper.insert(order);
// 扣减库存
this.updateProductStockById(order.getProductId(), 1L);
return new ResponseEntity(HttpStatus.OK);
}
/**
* 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateProductStockById(Integer num, Long productId) {
try {
productMapper.updateProductStockById(num, productId);
} catch (Exception e) {
// 这里仅仅是捕获异常之后的打印(相当于程序吞掉了异常)
log.error("Error updating product Stock: {}", e);
}
}
}
7. 数据库不支持事务
Spring事务生效的前提是连接的数据库支持事务,如果底层的数据库都不支持事务,则Spring事务肯定会失效的,例如🌰:使用MySQL数据库,选用MyISAM
存储引擎,因为MyISAM
存储引擎本身不支持事务,因此事务毫无疑问会失效。
8. 未配置开启事务
如果项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效,例如,如果你是Spring Boot项目,没有在SpringBoot项目中配置如下代码:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
如果是以往的Spring MVC项目,如果没有配置下面的代码,Spring事务也不会生效,正常需要在applicationContext.xml文件中,手动配置事务相关参数,比如:
<!-- 配置事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 用切点把事务切进去 -->
<aop:config>
<aop:pointcut expression="execution(* com.universal.ubdk.*.*(..))" id="pointcut"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>
9. 错误的传播特性
其实,我们在使用@Transactional
注解时,是可以指定propagation
参数的。
该参数的作用是指定事务的传播特性,目前Spring支持7种传播特性:
-
REQUIRED
如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。 -
SUPPORTS
如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。 -
MANDATORY
如果当前上下文中存在事务,否则抛出异常。 -
REQUIRES_NEW
每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。 -
NOT_SUPPORTED
如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。 -
NEVER
如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。 -
NESTED
如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
如果我们在手动设置propagation
参数的时候,把传播特性设置错了,比如:
@Service
public class OrderServiceImpl {
@Transactional(propagation = Propagation.NEVER)
public void cancelOrder(UserModel userModel) {
// 取消订单
cancelOrder(orderDTO);
// 还原库存
restoreProductStock(orderDTO.getProductId(), orderDTO.getProductCount());
}
}
我们可以看到cancelOrder()
方法的事务传播特性定义成了Propagation.NEVER
,这种类型的传播特性不支持事务,如果有事务则会抛异常。
10. 多线程调用
在实际项目开发中,多线程的使用场景还是挺多的。如果Spring事务用在多线程场景中使用不当,也会导致事务无法生效。
@Slf4j
@Service
public class OrderServiceImpl {
@Autowired
private OrderMapper orderMapper;
@Autowired
private MessageService messageService;
@Transactional
public void orderCommit(orderModel orderModel) throws Exception {
orderMapper.insertOrder(orderModel);
new Thread(() -> {
messageService.sendSms();
}).start();
}
}
@Service
public class MessageService {
@Transactional
public void sendSms() {
// 发送短信
}
}
通过示例,我们可以看到订单提交的事务方法orderCommit()
中,调用了发送短信的事务方法sendSms()
,但是发送短信的事务方法sendSms()
是另起了一个线程调用的。
这样会导致两个方法不在同一个线程中,从而是两个不同的事务。如果是sendSms()
方法中抛了异常,orderCommit()
方法也回滚是不可能的。
实际上,Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定,多个线程自然会让事务失效。

总结
本篇文章主要是介绍Spring事务传播特性,阐明了@Transactional
注解属性的使用方式,通过不同的代码示例演示了Spring事务失效的常见场景,如果文章对你有所帮助,欢迎 点赞👍+评论💬+收藏❤
作者:austin流川枫
来源:juejin.cn/post/7198894062534475834
推荐
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
原文始发于微信公众号(Java知音):我认真总结并分析了 Spring 事务失效的十种常见场景
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/106438.html
评论列表(41条)
Fantastic goods from you, man. I have understand your stuff previous to and you are
just extremely fantastic. I really like what you have acquired
here, certainly like what you are saying and the way in which you say it.
You make it entertaining and you still take care
of to keep it wise. I can’t wait to read far more from you.
This is really a tremendous site.
Thanks for sharing such a fastidious opinion, article
is fastidious, thats why i have read it fully
Hey there! I understand this is kind of off-topic however I had to ask.
Does managing a well-established blog such as yours
require a large amount of work? I’m brand new to
operating a blog however I do write in my journal on a daily basis.
I’d like to start a blog so I can share my personal experience and feelings online.
Please let me know if you have any kind of suggestions or tips for brand new aspiring bloggers.
Appreciate it!
@trailers repair service minneapolis mn:This is a kind of fun. It is recommended that you use WordPress, which is very simple and not complicated.
hey there and thank you for your info – I’ve certainly picked up anything
new from right here. I did however expertise a few technical points using this web site, since I experienced to reload the site a lot of times previous to I could get it to load correctly.
I had been wondering if your web host is OK? Not that I’m complaining, but
slow loading instances times will sometimes affect your placement in google and can damage your
high quality score if advertising and marketing with Adwords.
Anyway I’m adding this RSS to my e-mail and could look out for a lot more of your respective exciting content.
Ensure that you update this again soon.
@slot Gacor malam ini:Recently, the number of visits has suddenly increased. I am trying to do some optimization, hoping to bring you a better use experience.
I am really grateful to the holder of this web page who has shared this enormous article at at this place.
my homepage bar offside tavern New York; offsidetavernnyc.com,
Goood post. I learn something totally new and challenging on websites I stumbleupon onn a daily basis.
It’s always interesting to read through articles from other authors and practice something from their websites.
My homepage … animal rescue
I think this is one of the most important info for me.
And i’m glad reading your article. But should remark on few general things, The web site style is ideal, the articles is really
excellent : D. Good job, cheers
Thanks for a marvelous posting! I seriously enjoyed reading it, you may be a great author.I will ensure that
I bookmark your blog and definitely will come back
in the foreseeable future. I want to encourage you continue your great job, have a nice afternoon!
I got this website from my buddy who shared with
me concerning this site and now this time I am browsing this site and reading very informative articles at this place.
Hey terrific blog! Does running a blog such as this
take a large amount of work? I’ve virtually no expertise in computer programming however I
was hoping to start my own blog in the near future.
Anyhow, should you have any recommendations or tips for new blog owners please share.
I understand this is off subject but I just wanted to ask.
Appreciate it!
Hi this is somewhat of off topic but I was wondering if blogs use WYSIWYG editors or if you have
to manually code with HTML. I’m starting a blog soon but have no coding skills so I wanted to
get advice from someone with experience. Any help would be greatly appreciated!
Magnificent web site. Lots of helpful info here. I am sending it to some buddies
ans also sharing in delicious. And of course, thanks to your sweat!
Thankfulness to my father who told me on the topic of this weblog, this weblog is actually
amazing.
Your way of describing the whole thing in this article is truly fastidious,
all be capable of easily know it, Thanks a lot.
Hi there, this weekend is nice in support of me, since this moment
i am reading this impressive informative article here at my house.
First of all I want to say fantastic blog! I had a quick question in which
I’d like to ask if you don’t mind. I was interested to find out how you center yourself and clear your mind before writing.
I’ve had difficulty clearing my thoughts in getting my thoughts out.
I truly do take pleasure in writing however it just seems like
the first 10 to 15 minutes are lost simply just trying to figure out how to begin.
Any suggestions or hints? Cheers!
It’s great that you are getting ideas from this article as
well as from our argument made at this place.
I constantly emailed this weblog post page to all my associates,
since if like to read it next my friends will too.
I simply could not go away your web site prior
to suggesting that I really loved the standard info a
person supply for your guests? Is gonna be again often in order to check up on new posts
I’m extremely inspired with your writing abilities as neatly as with the layout in your weblog.
Is this a paid topic or did you modify it yourself? Anyway keep
up the nice high quality writing, it is rare to peer a great weblog like this one today..
What’s up, its good piece of writing on the topic of media
print, we all know media is a impressive source of data.
You made some decent points there. I checked on the web
to learn more about the issue and found most
people will go along with your views on this website.
Today, I went to the Ьeachfront with my chilԀren. I found a
seɑ shell and gaνe it too my 4 ʏеar old
daughter and said “You can hear the ocean if you put this to your ear.”
She put the shell to her ear and screamed.
There was a hermit crab inside and it pijched her ear.
Sһe never wants to go back! LoL I know this is entirely
off topic but I hadd to tеll someone!
Have a look at myy blog post – Tranh gỗ cao cấp
You need to be a part of a contest for one of the greatest blogs on the web.
I am going to recommend this web site!
Greetings from Ohio! I’m bored to tears at work
so I decided to browse your blog on my iphone during lunch break.
I enjoy the information you provide here
and can’t wait to take a look when I get home. I’m surprised at how quick your
blog loaded on my mobile .. I’m not even using WIFI, just 3G ..
Anyways, superb blog!
I will immediately seize your rss as I can not in finding
your e-mail subscription link or newsletter service.
Do you have any? Please permit me recognise so that I could subscribe.
Thanks.
whoah this weblog is fantastic i really like studying your posts.
Keep up the great work! You understand, lots of people are searching around for this information, you could aid them greatly.
Wow that was unusual. I just wrote an really long comment but after I clicked submit my comment didn’t
show up. Grrrr… well I’m not writing all that over again. Anyway, just
wanted to say superb blog!
My brother suggested I would possibly like this blog. He was once totally right.
This publish actually made my day. You can not imagine simply how a lot time I
had spent for this information! Thanks!
Howdy, I believe your website could possibly be having internet browser compatibility problems.
Whenever I take a look at your web site in Safari, it
looks fine however, if opening in Internet Explorer,
it’s got some overlapping issues. I just wanted to provide you with a quick heads up!
Aside from that, wonderful site!
Greetings! Very useful advice in this particular article!
It is the little changes that produce the largest changes.
Thanks a lot for sharing!
Preciserly ѡhat I waѕ loߋking for, regardss foor
posting.
I get pleasure from, result in I found exactly what I was taking a look
for. You have ended my 4 day long hunt! God Bless you man. Have a nice day.
Bye
Ahaa, its fastidious conversation concerning this article
here at this web site, I have read all that, so at this time me also
commenting at this place.
Yes! Finally someone writes about expression med dexcom.
My brother suggested I might like this web site. He was entirely right.
This post actually made my day. You can not imagine simply how so much
time I had spent for this info! Thanks!
Hello just wanted to give you a quick heads up. The text in your content
seem to be running off the screen in Opera. I’m not sure if this is a formatting issue or something
to do with internet browser compatibility but I thought I’d
post to let you know. The style and design look great though!
Hope you get the issue fixed soon. Cheers
hey there and thank you for your information – I have certainly picked
up something new from right here. I did however expertise
several technical points using this web site, as I experienced to reload the site a lot of
times previous to I could get it to load correctly.
I had been wondering if your hosting is OK? Not that I’m
complaining, but sluggish loading instances times will often affect
your placement in google and could damage your high quality score if advertising and marketing with Adwords.
Well I am adding this RSS to my e-mail and can look
out for much more of your respective fascinating content.
Ensure that you update this again very soon.
An outstanding share! I have just forwarded this onto a friend who has been doing a little research
on this. And he actually ordered me lunch due to the fact that I discovered it for him…
lol. So let me reword this…. Thanks for the meal!!
But yeah, thanks for spending some time to talk about this matter here on your website.