一文搞清楚Spring事务

 更新时间:2023年04月24日 08:59:51   作者:雷哥不爱写代码  
Spring事务是指在Spring框架中对于数据库操作的一种支持,它通过对一组数据库操作进行整体控制来保证数据的一致性和完整性。本文介绍Spring事务介绍的非常详细,有需要的朋友可以参考本文

什么是Spring事务?

Spring事务是指在Spring框架中对于数据库操作的一种支持,它通过对一组数据库操作进行整体控制来保证数据的一致性和完整性。Spring事务可以保证在一组数据库操作执行时,要么所有操作都执行成功,要么所有操作都回滚到之前的状态,从而避免了数据不一致的情况。

Spring事务实现方式

Spring事务可以通过编程式事务和声明式事务两种方式来实现。编程式事务需要在代码中手动控制事务的开始、提交和回滚等操作,而声明式事务则是通过在配置文件中声明事务的切入点和通知等信息来自动控制事务的行为。

Spring编程式事务

Spring编程式事务需要在代码中获取事务管理器,并通过该事务管理器获取事务对象,然后使用该事务对象来控制事务的开始、提交和回滚等操作。

public void transferMoney(Account fromAccount, Account toAccount, double amount) {
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
        fromAccount.withdraw(amount);
        toAccount.deposit(amount);
        transactionManager.commit(status);
    } catch (Exception e) {
        transactionManager.rollback(status);
    }
}

在上面的代码中,首先通过transactionManager.getTransaction方法获取事务对象status,然后在try块中执行转账操作,最后通过transactionManager.commit(status)提交事务。如果在转账操作中发生了异常,则会通过transactionManager.rollback(status)回滚事务。

编程式事务的优点是灵活性高,可以根据具体的业务需求来灵活控制事务的行为。不过缺点是代码冗长,可读性差,而且容易出现错误。

Spring声明式事务

Spring声明式事务需要在配置文件中声明事务管理器、事务通知等元素,然后在需要使用事务的方法上添加事务切面的注解即可。

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="transferMoney" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="transferPointcut" expression="execution(* com.example.TransferService.transferMoney(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transferPointcut"/>
</aop:config>

在上面的配置文件中,首先声明了事务管理器transactionManager,然后定义了事务通知txAdvice,该通知会在transferMoney方法执行时进行事务管理。最后通过aop:configaop:advisor来将txAdvice应用于transferPointcut定义的切入点上。

声明式事务的优点是通过配置文件来管理事务,避免了在代码中手动控制事务的繁琐和容易出错的问题。不过缺点是灵活性较差,不能根据具体的业务需求来灵活控制事务的行为。

声明式事务注解方式

Spring声明式事务也可以通过注解的方式来实现。具体来说,可以在需要使用事务的方法上添加@Transactional注解,并通过该注解的属性来指定事务的传播机制、隔离级别、超时时间等信息。

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600)
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
    fromAccount.withdraw(amount);
    toAccount.deposit(amount);
}

在上面的代码中,通过@Transactional注解来声明了事务的传播机制为Propagation.REQUIRED,隔离级别为Isolation.DEFAULT,超时时间为3600秒。在方法执行时,Spring会根据注解中的信息自动管理事务的行为。

注解式声明事务的优点是代码简洁、可读性好,灵活性和可用性高,容易维护和调试。但是它也有一些缺点,例如注解的使用可能会导致代码分散,而且无法通过配置文件来管理事务,而且可能会引入一些意想不到的问题。

@Transactional注解的常用参数包括:

  • value:该属性可以用来指定事务管理器的名称,如果只有一个事务管理器,则可以省略该属性。
  • propagation:该属性用于指定事务的传播机制,包括REQUIREDSUPPORTSMANDATORYREQUIRES_NEWNOT_SUPPORTEDNEVERNESTED共7种。
  • isolation:该属性用于指定事务的隔离级别,包括DEFAULTREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE共5种。
  • timeout:该属性用于指定事务的超时时间,单位为秒,默认值为-1,表示没有超时限制。
  • readOnly:该属性用于指定事务是否为只读事务,默认值为false,表示事务可读可写。
  • rollbackFor:该属性用于指定事务回滚的条件,如果出现指定的异常类型,则事务会回滚。
  • noRollbackFor:该属性用于指定事务不回滚的条件,如果出现指定的异常类型,则事务不会回滚。

除了上述常用参数外,@Transactional注解还有其他一些参数,例如transactionManagerrollbackForClassNamenoRollbackForClassNamevalue等,具体用法可以参考Spring官方文档。

在使用@Transactional注解时,需要根据具体的业务需求来选择合适的参数,以保证事务的正确性和可靠性。

事务注解失效情况

Spring的声明式事务注解可能失效的情况包括:

  • 注解被错误地放置在了类上而不是方法上。
  • 方法是private或final的,而且它们不能从外部调用。
  • 方法没有被公开暴露,也就是说,它们不是公开的方法(public)。
  • 被注解的方法是静态的。
  • 被注解的方法依赖于其他未被注解的方法(例如,被注解的方法调用了一个未被注解的私有方法)。
  • 被注解的方法在另一个类中被调用。
  • 注解被错误地配置了,例如使用了错误的名称或参数。
  • 类没有被正确地扫描,因此Spring无法找到应该被注解的方法。
  • 注解属性propagation设置错误
  • 注解属性rollbackFor设置错误
  • 异常被catch捕获导致@Transactional失效
  • 数据库引擎不支持事务
  • 多个切面的影响也会导致事务失效

如果遇到声明式事务注解失效的情况,需要检查上述问题并进行相应的修复。

代码规范中强调

@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的敌方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎、消息补偿、统计修正等。

在使用Spring事务时,需要注意对于事务的传播机制和隔离级别的设置,以及对于事务的异常处理等问题。正确地使用Spring事务可以提高系统的数据一致性和可靠性。

什么是Spring事务传播机制

Spring事务传播机制是指在多个事务操作发生时,如何管理这些操作之间的事务关系。Spring事务传播机制可以通过Propagation枚举类中的不同值来指定,共包括七种不同的传播行为。具体来说,Spring事务传播机制包括以下七种:

  • REQUIRED:如果当前没有事务,则创建一个新的事务;如果当前已经存在事务,则加入该事务。这是默认的传播行为。
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。
  • MANDATORY:必须在一个已存在的事务中执行,否则就抛出TransactionRequiredException异常。
  • REQUIRES_NEW:创建一个新的事务,并在该事务中执行;如果当前存在事务,则将当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将当前事务挂起。
  • NEVER:以非事务方式执行操作,如果当前存在事务,则抛出IllegalTransactionStateException异常。
  • NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则创建一个新的事务。

在使用Spring事务传播机制时,需要根据具体的业务需求来选择合适的传播行为,以保证事务的正确性和可靠性。同时,需要注意事务的异常处理等问题,以避免数据不一致或丢失的情况发生。

@Transactional注解中的传播机制

除了在代码中使用编程式事务和声明式事务外,Spring还提供了@Transactional注解来实现事务控制。在使用@Transactional注解时,可以通过propagation属性来指定事务的传播机制,例如:

@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Account fromAccount, Account toAccount, double amount) {
    fromAccount.withdraw(amount);
    toAccount.deposit(amount);
}

在上面的代码中,通过propagation属性来指定了事务的传播机制为Propagation.REQUIRED,即如果当前没有事务,则创建一个新的事务;如果当前已经存在事务,则加入该事务。

除了Propagation枚举类中的七种传播行为外,@Transactional注解还可以通过isolationtimeoutreadOnly等属性来指定事务的隔离级别、超时时间和只读事务等信息。

在使用@Transactional注解时,需要根据具体的业务需求来选择合适的传播行为和其他属性,以保证事务的正确性和可靠性。

Spring事务的隔离级别

Spring事务的隔离级别是指多个事务之间的隔离程度,它可以通过设置isolation属性来指定。Spring事务的隔离级别包括以下五种:

  • DEFAULT:默认的隔离级别,由底层数据库引擎决定。
  • READ_UNCOMMITTED:最低的隔离级别,允许读取未提交的数据。该级别会导致“脏读”、“不可重复读”和“幻读”等问题。
  • READ_COMMITTED:只允许读取已经提交的数据。该级别可以避免“脏读”,但可能会导致“不可重复读”和“幻读”等问题。
  • REPEATABLE_READ:保证在同一个事务中多次读取同一数据时,该数据的值不会发生变化。该级别可以避免“脏读”和“不可重复读”,但可能会导致“幻读”等问题。
  • SERIALIZABLE:最高的隔离级别,强制事务串行执行,避免了“脏读”、“不可重复读”和“幻读”等问题。但是该级别会对性能产生较大的影响,因此一般不建议使用。

在选择隔离级别时,需要根据具体的业务需求来选择合适的级别。一般来说,如果不需要在事务中读取未提交的数据,那么可以选择READ_COMMITTED级别;如果需要避免“不可重复读”问题,可以选择REPEATABLE_READ级别;如果需要避免“幻读”问题,可以选择SERIALIZABLE级别。但是需要注意的是,隔离级别越高,事务的并发性越差,因此需要根据具体业务场景来权衡隔离级别和性能。

不可重复读和幻读的区别

不可重复读和幻读都是在并发读写数据时可能出现的问题。

不可重复读指的是在一个事务中多次读取同一数据,但是由于其他事务的修改,导致两次读取的结果不一致。例如,事务A在读取某个数据时,事务B修改了该数据并提交,然后事务A再次读取该数据时,得到了不同的结果。

幻读是指在一个事务中多次读取同一范围内的数据,但是由于其他事务的插入操作,导致两次读取的结果不一致。例如,事务A在读取某个范围内的数据时,事务B插入了一条数据并提交,然后事务A再次读取该范围内的数据时,得到了不同的结果。

不可重复读和幻读的区别在于,不可重复读是在读取同一数据时出现问题,而幻读是在读取同一范围内的数据时出现问题。解决不可重复读的问题可以使用锁机制或者提高事务的隔离级别,而解决幻读的问题可以使用锁机制或者使用更高的隔离级别,例如SERIALIZABLE级别。

rollbackFor属性

在使用@Transactional注解进行声明式事务管理时,可以通过rollbackFor属性来指定哪些异常类型需要回滚事务。需要注意的是,rollbackFor属性指定的异常类型必须是Throwable类型或其子类,并且必须包含在Spring事务抛出的异常类型之内。如果指定的异常类型不正确或不在事务回滚的异常类型之内,可能会导致事务无法正确回滚或者回滚不完整的问题。

如果需要指定多个异常类型,可以使用数组的方式进行指定,例如:

@Transactional(rollbackFor = {SQLException.class, IOException.class})
public void doSomething() {
    // ...
}

在上面的代码中,指定了SQLExceptionIOException两种异常类型需要回滚事务。默认情况只有RuntimeException和Error会触发事务回滚。

除了rollbackFor属性外,@Transactional注解还有其他一些属性可以用于指定事务的属性和行为,包括propagationisolationtimeoutreadOnly等。需要根据具体的业务需求来选择合适的属性和行为,以保证事务的正确性和可靠性。

到此这篇关于一文搞清楚Spring事务的文章就介绍到这了,更多相关Java Spring事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java发送post方法详解

    Java发送post方法详解

    这篇文章主要介绍了Java发送post方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java线程的五种状态介绍

    Java线程的五种状态介绍

    本文主要为大家详细介绍一下Java实现线程创建的五种写法,文中的示例代码讲解详细,对我们学习有一定的帮助,感兴趣的可以跟随小编学习一下
    2022-08-08
  • linux下执行java程序的sh脚本分享

    linux下执行java程序的sh脚本分享

    这篇文章主要介绍了linux下执行java程序的sh脚本,仅供参考,但是设置的时候环境变量是最重要的,我就是环境变量一直不对,总是按网上查到的来,不明白怎么回事,才一直出错,其实环境变量就是你要执行的java程序所在的位置
    2014-09-09
  • 在IntelliJ IDEA中创建和运行java/scala/spark程序的方法

    在IntelliJ IDEA中创建和运行java/scala/spark程序的方法

    这篇文章主要介绍了在IntelliJ IDEA中创建和运行java/scala/spark程序的教程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • MyBatis在注解上使用动态SQL方式(@select使用if)

    MyBatis在注解上使用动态SQL方式(@select使用if)

    这篇文章主要介绍了MyBatis在注解上使用动态SQL方式(@select使用if),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • SpringCloud OpenFeign 服务调用传递 token的场景分析

    SpringCloud OpenFeign 服务调用传递 token的场景分析

    这篇文章主要介绍了SpringCloud OpenFeign 服务调用传递 token的场景分析,本篇文章简单介绍 OpenFeign 调用传递 header ,以及多线程环境下可能会出现的问题,其中涉及到 ThreadLocal 的相关知识,需要的朋友可以参考下
    2022-07-07
  • RxJava2.x+ReTrofit2.x多线程下载文件的示例代码

    RxJava2.x+ReTrofit2.x多线程下载文件的示例代码

    本篇文章主要介绍了RxJava2.x+ReTrofit2.x多线程下载文件的示例代码,具有一定的参考价值,有兴趣的可以了解一下
    2017-09-09
  • java多线程编程之Synchronized关键字详解

    java多线程编程之Synchronized关键字详解

    这篇文章主要为大家详细介绍了java多线程编程之Synchronized关键字,感兴趣的朋友可以参考一下
    2016-05-05
  • SpringMVC4.3 HttpMessageConverter接口实现源码分析

    SpringMVC4.3 HttpMessageConverter接口实现源码分析

    这篇文章主要为大家介绍了SpringMVC4.3 HttpMessageConverter接口实现源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Ubuntu安装jdk8常用方法流程解析

    Ubuntu安装jdk8常用方法流程解析

    这篇文章主要介绍了Ubuntu安装jdk8常用方法流程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11

最新评论