spring 事务实现方式有哪些?

Spring事务主要分为编程式和声明式两大类,前者通过PlatformTransactionManager或TransactionTemplate在代码中手动控制事务,后者通过@Transactional注解结合AOP实现事务管理,具有低侵入性和高可维护性,是现代Spring应用的首选方式。

spring 事务实现方式有哪些?

Spring事务的实现方式,说白了,主要就两大类:一种是编程式事务,另一种是声明式事务。在我个人的经验里,声明式事务因为其便捷性和低侵入性,几乎成了现代Spring应用的首选,但了解编程式事务的底层逻辑也绝不是坏事。

解决方案

Spring框架在事务管理上提供了强大的支持,核心在于抽象了底层事务API(如JDBC、JTA、JPA等),提供了一致的编程模型。

1. 编程式事务管理这种方式需要你在代码中显式地调用事务API来管理事务的开始、提交和回滚。它提供了最细粒度的控制,但缺点是代码侵入性强,容易产生大量重复代码。

使用

PlatformTransactionManager

这是Spring事务抽象的核心接口。你需要注入一个

PlatformTransactionManager

实例,然后手动创建

TransactionStatus

对象,并在try-catch-finally块中进行事务的提交或回滚。

@Servicepublic class MyService {    private final PlatformTransactionManager transactionManager;    public MyService(PlatformTransactionManager transactionManager) {        this.transactionManager = transactionManager;    }    public void doSomethingTransactional() {        DefaultTransactionDefinition def = new DefaultTransactionDefinition();        TransactionStatus status = transactionManager.getTransaction(def);        try {            // 业务逻辑操作            // 例如:dao.insertDataA(); dao.updateDataB();            transactionManager.commit(status);        } catch (Exception e) {            transactionManager.rollback(status);            throw new RuntimeException("Transaction failed", e);        }    }}

使用

TransactionTemplate

这是Spring提供的一个模板类,它封装了事务的创建、提交、回滚等boilerplate代码,让你的业务逻辑更聚焦。它在内部还是使用了

PlatformTransactionManager

@Servicepublic class MyService {    private final TransactionTemplate transactionTemplate;    public MyService(PlatformTransactionManager transactionManager) {        this.transactionTemplate = new TransactionTemplate(transactionManager);    }    public void doSomethingTransactionalWithTemplate() {        transactionTemplate.execute(status -> {            try {                // 业务逻辑操作                // 例如:dao.insertDataC(); dao.deleteDataD();                return "Success";            } catch (Exception e) {                status.setRollbackOnly(); // 标记事务为回滚                throw new RuntimeException("Transaction failed", e);            }        });    }}

2. 声明式事务管理这是Spring推荐的方式,通过AOP(面向切面编程)实现。你不需要在业务代码中显式编写事务管理逻辑,只需通过配置(XML或注解)来声明哪些方法需要事务支持。Spring会在运行时通过代理为这些方法织入事务管理功能。

基于XML配置: 早期项目或一些特定场景下可能会用到。通过


定义事务通知,然后用


将其织入到目标方法。这种方式配置相对繁琐,可读性也不如注解直观。

基于

@Transactional

注解: 这是目前最主流、最推荐的方式。你只需要在类或方法上添加

@Transactional

注解,Spring就会自动为其创建事务代理。

当你在方法上加上

@Transactional

注解时,Spring会创建一个代理对象,在方法执行前开启事务,方法执行成功后提交事务,如果抛出运行时异常(

RuntimeException

Error

)则回滚事务。你也可以通过注解的属性来控制事务的行为:

propagation

:事务的传播行为(如

REQUIRED

,

REQUIRES_NEW

等)。

isolation

:事务的隔离级别(如

READ_COMMITTED

,

REPEATABLE_READ

等)。

timeout

:事务的超时时间。

readOnly

:是否为只读事务,可以优化性能。

rollbackFor

:指定哪些异常类型需要回滚。

noRollbackFor

:指定哪些异常类型不需要回滚。

@Servicepublic class UserService {    // 假设有用户数据访问对象    // private final UserRepository userRepository;    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)    public void createUserAndAssignRole(User user, Role role) {        // userRepository.save(user);        // roleRepository.assign(user.getId(), role.getId());        // 如果这里有任何异常,整个方法的操作都会回滚    }    @Transactional(readOnly = true)    public User getUserById(Long id) {        // return userRepository.findById(id);        return null;    }}

在我看来,

@Transactional

注解的简洁性简直是开发者的福音,它让业务代码保持纯粹,极大地提升了开发效率和代码可维护性。

Spring事务的传播行为有哪些,如何理解?

事务传播行为,这玩意儿听起来有点玄乎,但其实就是当一个方法调用另一个方法时,这两个方法的事务如何相互作用的规则。理解这些规则,对于避免一些难以察觉的事务问题至关重要。Spring定义了七种传播行为,但日常开发中常用的也就那么几种。

REQUIRED

(默认也是最常用):如果当前存在事务,就加入该事务;如果当前没有事务,就创建一个新的事务。

理解: 想象你正在进行一项任务(事务A),这时你需要调用一个子任务(方法B)。如果子任务B也要求在事务中运行,那么它会“搭上”你当前任务A的事务这趟车。如果任务A没有事务,子任务B会自己开一辆新车。这是最安全、最常用的选择,确保方法总是在事务环境中运行。

SUPPORTS

:如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行。

理解: 子任务B对事务持“支持”态度。有事务就跟着走,没事务也不强求,自己干自己的活。适合那些可有可无事务支持的操作,比如查询。

MANDATORY

:如果当前存在事务,就加入该事务;如果当前没有事务,就抛出异常。

理解: 子任务B是“强制”要求有事务的。如果没有事务,它就直接罢工(抛异常)。这适用于那些必须在事务环境下才能正确执行的关键操作。

REQUIRES_NEW

:总是创建一个新的事务,如果当前存在事务,就将当前事务挂起。

理解: 子任务B是个“独立”的个体,它不关心你有没有事务,它每次都会开一辆新车,并且把你原来的车(事务)暂时停在一边。子任务B自己的事务提交或回滚,不会影响到它外部的事务。这在需要确保某个操作独立提交或回滚时非常有用,比如日志记录。

NOT_SUPPORTED

:以非事务方式执行操作,如果当前存在事务,就将当前事务挂起。

理解: 子任务B“不支持”事务。如果它发现你带着事务来了,它会让你把事务先放一边,自己以非事务方式执行。适合那些不需要事务,甚至会干扰事务的操作。

NEVER

:以非事务方式执行操作,如果当前存在事务,就抛出异常。

理解: 子任务B“绝不”允许有事务。如果你带着事务来了,它就直接报错。这比

NOT_SUPPORTED

更严格。

NESTED

:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则创建一个新的事务。

火山方舟 火山方舟

火山引擎一站式大模型服务平台,已接入满血版DeepSeek

火山方舟 99 查看详情 火山方舟 理解: 嵌套事务,它有点像

REQUIRED

,但又不同。它会在父事务中创建一个“保存点”(savepoint)。如果嵌套事务回滚,它只会回滚到这个保存点,而不会影响到父事务的提交或回滚。但如果父事务回滚,它会连同嵌套事务一起回滚。这需要底层数据库支持保存点。我个人觉得,这个用得相对较少,而且理解起来也容易混淆,实际项目中我倾向于用

REQUIRES_NEW

来明确隔离。

为什么我的Spring事务不生效?常见陷阱与排查。

说实话,

@Transactional

注解虽然好用,但有时它就是不生效,这真是让人抓狂。遇到这种情况,别急着骂Spring,多半是有些细节没注意到。这里我总结几个常见的“坑”和排查思路:

方法不是

public

的: 这是最常见的。Spring的AOP代理(无论是JDK动态代理还是CGLIB代理)默认只对

public

方法生效。如果你把

@Transactional

放在

private

protected

或默认(包可见)方法上,它是不会生效的。

排查: 检查你的事务方法是不是

public

的。

同一个类中方法A调用方法B(自调用问题): 这是一个非常经典的陷阱。当你在同一个Service类中,一个没有

@Transactional

注解的方法A调用了另一个有

@Transactional

注解的方法B时,事务可能不会生效。

原因: Spring的事务是通过代理实现的。当你通过

this

关键字调用同一个类中的方法时,实际上是绕过了Spring生成的代理对象,直接调用了目标对象的方法。这样一来,代理在方法B上织入的事务逻辑就无法生效了。

排查:

@Servicepublic class MyService {    @Transactional // 这个事务不会生效    public void methodB() {        System.out.println("Method B executed.");        // ... 业务逻辑 ...    }    public void methodA() {        System.out.println("Method A executed.");        this.methodB(); // 直接通过this调用,绕过了代理    }}

解决方案:

methodB

移到一个独立的Service类中。通过Spring上下文获取当前Service的代理对象来调用(不推荐,代码丑陋)。使用

AopContext.currentProxy()

获取当前代理对象,然后调用

((MyService) AopContext.currentProxy()).methodB();

(需要开启

exposeProxy = true

)。我个人更倾向于第一种,职责分离也更清晰。

异常类型不对,或者异常被捕获了: 默认情况下,Spring事务只对运行时异常(

RuntimeException

及其子类)和

Error

进行回滚。如果你抛出的是受检异常(Checked Exception,如

IOException

SQLException

等),而又没有明确配置

@Transactional(rollbackFor = MyCheckedException.class)

,事务是不会回滚的。

排查: 检查你的业务逻辑中抛出的异常类型。如果捕获了异常,确保在捕获后重新抛出运行时异常,或者手动调用

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

来标记回滚。

数据库不支持事务: 某些数据库存储引擎(比如MySQL的MyISAM)是不支持事务的。虽然Spring的事务配置没问题,但底层数据库不支持,自然也就无法实现事务。

排查: 检查你的数据库表使用的存储引擎(例如MySQL的InnoDB是支持事务的)。

没有启用Spring的事务管理: 忘记在Spring配置中启用事务管理,比如没有

@EnableTransactionManagement

注解(在Spring Boot中通常会自动配置),或者XML配置中没有


排查: 检查你的Spring配置类或XML文件。

事务方法内部调用了非事务方法,且非事务方法抛出了异常: 如果一个事务方法内部调用了一个没有

@Transactional

注解的方法,而这个非事务方法抛出了异常,并且这个异常没有被事务方法捕获,那么事务会回滚。但如果异常被事务方法捕获了,且没有再次抛出运行时异常或标记回滚,事务就不会回滚。

排查: 仔细审查异常的传播路径和捕获逻辑。

编程式事务与声明式事务,我该如何选择?

这其实是一个经典的取舍问题,但现代应用开发中,答案往往偏向一边。

在我看来,绝大多数情况下,你应该选择声明式事务,特别是基于

@Transactional

注解的方式。 理由很简单:

代码整洁度: 声明式事务将事务管理逻辑与业务逻辑彻底分离。你的业务代码只关心业务本身,不需要掺杂任何事务相关的API调用。这让代码看起来更干净、更易读、更专注于核心业务。开发效率: 只需要一个注解,Spring就会为你处理所有事务的开启、提交、回滚,大大减少了重复代码的编写。这简直是生产力工具维护性: 当事务策略需要调整时(比如改变传播行为、隔离级别),你只需要修改注解的属性,而不需要深入到业务代码中去修改。这降低了维护成本和出错的可能性。

那么,什么时候会考虑编程式事务呢? 说实话,这种情况非常少见,但也不是没有。

极度细粒度的控制: 比如你需要在同一个方法内部,对不同的操作块应用不同的事务策略,甚至在某些特定条件下手动控制提交或回滚,而这些条件又非常复杂,无法通过声明式事务的属性来表达。但即使是这种场景,我也会先思考能否通过拆分方法或调整架构来用声明式实现。非Spring环境的事务集成: 比如你正在一个混合项目中,需要与一些非Spring管理的事务系统进行交互,或者你需要完全脱离Spring的事务抽象,直接操作底层的JDBC

Connection

事务。遗留系统改造: 某些老旧系统可能已经有了大量的编程式事务代码,在不进行大规模重构的情况下,继续沿用编程式可能是成本最低的选择。

总的来说,如果你正在开发一个新的Spring应用,或者对现有应用进行现代化改造,请毫不犹豫地拥抱

@Transactional

。它不仅能让你的代码更优雅,也能让你从繁琐的事务管理细节中解脱出来,把精力放在真正有价值的业务逻辑上。编程式事务更像是一个“备用方案”,在极少数特殊场景下才需要考虑。

以上就是spring 事务实现方式有哪些?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/594573.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月10日 17:31:31
下一篇 2025年11月10日 17:36:21

相关推荐

  • Python中如何测试代码?

    在python中进行代码测试主要通过单元测试和集成测试,辅以性能测试和覆盖率分析。1. 使用unittest模块进行单元测试,确保代码在各种场景下正确运行。2. 采用pytest框架进行更简洁的测试,并利用其强大的报告功能。3. 通过coverage.py检查测试覆盖率,发现未测试的代码路径。4. …

    好文分享 2025年12月14日
    000
  • Python中怎样使用pymongo?

    在python中使用pymongo可以轻松地与mongodb数据库进行交互。1)安装pymongo:pip install pymongo。2)连接到mongodb:from pymongo import mongoclient; client = mongoclient(‘mongod…

    2025年12月14日
    000
  • 怎样在Python中创建多进程程序?

    python实现多进程编程主要依赖multiprocessing模块。1)创建进程:使用multiprocessing.process类创建并启动进程。2)进程池:使用multiprocessing.pool管理进程池,方便并行执行任务。3)进程间通信:使用queue实现生产者-消费者模式进行数据交…

    2025年12月14日
    000
  • 怎样在Python中处理JSON数据?

    在python中处理json数据可以使用json模块。1)将python对象转换为json字符串使用json.dumps()方法。2)解析json字符串为python对象使用json.loads()方法。3)写入json数据到文件使用json.dump()方法。4)从文件中读取json数据使用jso…

    2025年12月14日
    000
  • Python中如何使用try-except?

    try-except是python中处理异常的机制。1)基本结构包括try、except、else和finally块。2)在循环中应将try块范围最小化以提高性能。3)避免滥用try-except掩盖逻辑错误,并尽量具体处理异常类型。 在Python中,try-except是处理异常的一种机制,允许…

    2025年12月14日
    000
  • 如何用Python读写CSV文件?

    用Python读写CSV文件是一项常见的任务,特别是在处理数据分析、数据科学或任何需要批量处理数据的场景中。今天我们就来深入探讨一下如何优雅地使用Python来读写CSV文件,并且分享一些我在实际项目中踩过的坑以及一些优化的小技巧。 当我们谈到读写CSV文件时,Python提供了一个非常方便的内置模…

    2025年12月14日
    000
  • Python中如何使用Redis缓存数据?

    在python中使用redis进行数据缓存可以通过以下步骤实现:1. 安装redis-py库:使用pip install redis。2. 连接到redis服务器:使用redis.redis(host=’localhost’, port=6379, db=0)创建连接。3. …

    2025年12月14日
    000
  • 如何用Python进行网络编程?

    用python进行网络编程的优势在于其丰富的库和简洁的语法,使得编程简单高效。1)python提供了socket、requests、asyncio等库,支持服务器端和客户端开发。2)通过实际代码示例,展示了如何使用socket库创建tcp服务器和客户端。3)异步编程使用asyncio库可提高并发连接…

    2025年12月14日
    000
  • Python的selenium库怎么使用?

    在探索Python的Selenium库之前,让我们先回答一个关键问题:为什么要使用Selenium?Selenium是一个强大的工具,用于自动化web浏览器的操作。它特别适合需要与网页进行复杂交互的场景,例如自动化测试、数据抓取和网页监控。使用Selenium,你可以模拟用户在浏览器中的各种操作,如…

    2025年12月14日
    000
  • Python中如何实现多线程?

    在python中实现多线程主要使用threading模块。1) 创建和管理线程使用threading.thread类。2) 注意全局解释器锁(gil)的影响,可能需要使用multiprocessing或numba绕过限制。3) 使用threading.lock等确保线程安全。4) 高级用法如thre…

    2025年12月14日
    000
  • 怎样在Python中生成测试覆盖率报告?

    在python中生成测试覆盖率报告可以使用coverage.py工具。具体步骤包括:1. 安装coverage.py:pip install coverage;2. 编写测试用例,通常放在tests文件夹中;3. 运行测试并收集数据:coverage run -m unittest discover…

    2025年12月14日
    000
  • Python中如何操作图像?

    在python中操作图像主要使用pillow库。1.安装pillow库:pip install pillow。2.基本操作:打开、显示和保存图像。3.图像变换:旋转、缩放和裁剪。4.图像处理:使用抗锯齿和滤镜效果。5.高级操作:图像拼接和合成。6.性能优化:使用低内存模式和numpy库。 在Pyth…

    2025年12月14日
    000
  • Python的kafka-python库怎么使用?

    kafka-python库的使用主要包括生产者和消费者两部分。1. 创建生产者并发送消息到主题,如producer.send(‘test-topic’, b’hello, kafka!’),注意消息需为字节格式。2. 创建消费者并读取消息,如for m…

    2025年12月14日
    000
  • Python中如何定义抽象方法?

    在python中,通过abc模块定义抽象方法。1)导入abc模块,使用abcmeta作为元类定义抽象基类。2)用@abstractmethod装饰器标记需实现的方法。3)子类必须实现这些方法,否则实例化会引发typeerror。 在Python中,定义抽象方法通常是通过使用abc模块来实现的。这个模…

    2025年12月14日
    000
  • Python中如何使用__call__方法使对象可调用?

    在python中,通过在类中定义__call__方法可以让对象变得可调用。1. 创建一个可调用类,如callableexample,通过__call__方法实现问候功能。2. 实现一个计数器类counter,利用__call__方法增加并返回计数值。3. 使用timer类作为装饰器,通过__call…

    2025年12月14日
    000
  • python网站入口 python网站入口直接打开入口

    Python 作为一门广泛应用于数据科学、机器学习、人工智能、Web 开发等领域的编程语言,其官方网站是每个 Python 开发者和学习者的必经之路。无论你是初学者还是经验丰富的开发者,Python 官方网站都提供了丰富的资源和工具,帮助你更好地学习和使用这门语言。通过 Python 官方网站,你可…

    2025年12月14日
    000
  • 现在python主要用于什么 当前主流应用场景

    python 主要用于数据科学与机器学习、web 开发、自动化和脚本编写、教育和初学者编程以及金融和量化交易。1) 数据科学与机器学习:python 凭借其强大的库生态系统,如 numpy、pandas、scikit-learn 和 tensorflow,成为数据科学和机器学习领域的首选语言。2) …

    2025年12月14日
    000
  • Python中怎样编写文档字符串?

    python中编写文档字符串的方法如下:1) 使用三个引号包围,放在定义之后;2) 内容可单行或多行;3) 描述函数作用、参数、返回值和异常。文档字符串提高了代码的可读性和维护性,是开发流程中不可或缺的一部分。 Python中怎样编写文档字符串?文档字符串(docstring)是Python中用来为…

    2025年12月14日
    000
  • python用来做什么 python常见用途说明

    python 的常见用途包括数据科学和机器学习、web 开发、自动化任务和脚本编写、游戏开发以及教育。1)在数据科学和机器学习中,python 的库如 numpy、pandas 等使数据处理和分析变得简洁高效。2)在 web 开发中,django 和 flask 等框架使得构建 web 应用变得简单…

    2025年12月14日
    000
  • 怎样在Python中实现协程?

    在python中,协程通过asyncio模块实现异步编程,提高i/o密集型应用性能。1)定义协程使用async def,2)使用await等待异步操作,3)通过asyncio.run运行主协程。使用协程可以并发处理多个任务,如网络请求,提升程序效率。 在Python中实现协程是一种高效的异步编程方式…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信