这篇文章主要讲解了“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:config和
aop: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:该属性用于指定事务的传播机制,包括
REQUIRED、
SUPPORTS、
MANDATORY、
REQUIRES_NEW、
NOT_SUPPORTED、
NEVER和
NESTED共7种。
isolation:该属性用于指定事务的隔离级别,包括
DEFAULT、
READ_UNCOMMITTED、
READ_COMMITTED、
REPEATABLE_READ和
SERIALIZABLE共5种。
timeout:该属性用于指定事务的超时时间,单位为秒,默认值为-1,表示没有超时限制。
readOnly:该属性用于指定事务是否为只读事务,默认值为false,表示事务可读可写。
rollbackFor:该属性用于指定事务回滚的条件,如果出现指定的异常类型,则事务会回滚。
noRollbackFor:该属性用于指定事务不回滚的条件,如果出现指定的异常类型,则事务不会回滚。
除了上述常用参数外,
@Transactional注解还有其他一些参数,例如
transactionManager、
rollbackForClassName、
noRollbackForClassName和
value等,具体用法可以参考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注解还可以通过
isolation、
timeout、
readOnly等属性来指定事务的隔离级别、超时时间和只读事务等信息。
在使用
@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() { // ... }
在上面的代码中,指定了
SQLException和
IOException两种异常类型需要回滚事务。默认情况只有RuntimeException和Error会触发事务回滚。
除了
rollbackFor属性外,
@Transactional注解还有其他一些属性可以用于指定事务的属性和行为,包括
propagation、
isolation、
timeout、
readOnly等。需要根据具体的业务需求来选择合适的属性和行为,以保证事务的正确性和可靠性。