目录
一. 前言
首先简单了解一下 Spring 中事务传播行为是什么?听起来很高端,但是真正用起来的时候,稍有不慎,就会让自己陷入困境之中,所以在使用之前,我们必须要十分耐心认真的学习它。
从名字理解起来,事务传播行为,既然为传播就肯定发生在两个实体之间,否则单个实体又如何发生行为呢。通俗点讲就是“一个巴掌拍不响”。下面是定义。
事务传播行为主要用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的事务中,该事务如何传播。这个概述可能不好理解,换句话就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
二. 事务传播机制分类
2.1. 概览
总结如下表:
事务传播行为类型 | 解释说明 |
---|---|
Propagation_Required | 表示被修饰的方法必须运行在事务中,如果当前方法没有事务,则新建一个事务;如果已经存在一个事务中,就加入到这个事务中,此类型是最常见的默认选择。 |
Propagation_Supports | 表示被修饰的方法不需要事务上下文,如果当前方法存在事务,则支持当前事务执行;如果当前没有事务,就以非事务方式执行。 |
Propagation_Mandatory | 表示被修饰的方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。 |
Propagation_Requires_New | 表示被修饰的方法必须运行在它自己的事务中,一个新的事务会被启动,如果调用者存在当前事务,则在该方法执行期间,当前事务会被挂起。 |
Propagation_Not_Supported | 表示被修饰的方法不应该运行在事务中,如果调用者存在当前事务,则该方法运行期间,当前事务将被挂起。 |
Propagation_Never | 表示被修饰的方法不应该运行在事务上下文中,如果调用者或者该方法中存在一个事务正在运行,则会抛出异常。 |
Propagation_Nested | 表示当前方法已经存在一个事务,那么该方法将会在嵌套事务中运行,嵌套的事务可以独立与当前事务进行单独地提交或者回滚,如果当前事务不存在,那么其行为与Propagation_Required一样。 |
2.2. 分类
1. 支持当前事务的传播机制:
- REQUIRED:如果当前存在事务,方法将在该事务中运行;否则,将创建一个新的事务。
- SUPPORTS:如果当前存在事务,方法将在该事务中运行;否则,以非事务的方式执行。
- MANDATORY:方法必须在一个已存在的事务中运行,否则将抛出异常。
2. 不支持当前事务的传播机制:
- REQUIRES_NEW:方法将创建一个新的事务,并在自己的事务中运行,如果当前存在事务,则将其挂起。
- NOT_SUPPORTED:方法将以非事务的方式运行,如果当前存在事务,则将其挂起。
- NEVER:方法不能在一个已存在的事务中运行,否则将抛出异常。
3. 嵌套事务的传播机制:
- NESTED:如果当前存在事务,则在嵌套事务中执行;如果没有事务,则行为类似于REQUIRED。嵌套事务是外部事务的一部分,可以独立地进行提交或回滚。
三. 事务传播机制详解
3.1. REQUIRED
REQUIRED:如果当前存在事务,方法将在该事务中运行;否则,将创建一个新的事务。这是默认的传播行为。如果嵌套的方法出现异常,整个事务将回滚。
当使用 REQUIRED 传播机制时,如果当前已存在事务,则方法将在该事务中运行;如果当前没有事务,则将创建一个新的事务。
下面是一个简单的示例示范如何使用 REQUIRED 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.REQUIRED)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 REQUIRED 传播机制。如果在调用该方法时已经存在一个事务,那么该方法将在该事务中运行。如果在调用该方法时没有事务,那么将创建一个新的事务。
假设在调用 performTransaction() 方法之前已经存在一个事务,那么在方法内部的数据库操作将在该事务中进行。如果任何一个数据库操作失败,事务将回滚,所有的改变将被撤销。
如果在调用 performTransaction() 方法之前没有事务,那么将创建一个新的事务。在方法内部的数据库操作将在这个新的事务中进行。如果任何一个数据库操作失败,事务将回滚,所有的改变将被撤销。
这样做的好处是,如果在一个业务方法中调用多个数据库操作,可以确保这些操作要么全部成功,要么全部失败。如果其中一个操作失败,可以回滚整个事务,保持数据的一致性。
3.2. SUPPORTS
SUPPORTS:当使用 SUPPORTS 传播机制时,如果当前已存在事务,则方法将在该事务中运行;如果当前没有事务,则方法将在非事务环境中运行。
下面是一个简单的示例示范如何使用 SUPPORTS 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.SUPPORTS)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 SUPPORTS 传播机制。如果在调用该方法时已经存在一个事务,那么该方法将在该事务中运行。如果在调用该方法时没有事务,那么将在非事务环境中运行。
假设在调用 performTransaction() 方法之前已经存在一个事务,那么在方法内部的数据库操作将在该事务中进行。如果任何一个数据库操作失败,事务将回滚,所有的改变将被撤销。
如果在调用 performTransaction() 方法之前没有事务,那么将在非事务环境中执行方法内部的数据库操作。即使其中一个数据库操作失败,也不会回滚,因为非事务环境下没有事务可回滚。
这样做的好处是,可以在需要的时候使用事务,而在不需要事务的情况下,方法可以在非事务环境中运行,提高性能。但是需要注意的是,在非事务环境中执行的数据库操作将无法回滚,可能会导致数据不一致的问题,因此需要谨慎使用 SUPPORTS 传播机制。
3.3. MANDATORY
MANDATORY:方法必须在一个已存在的事务中运行,否则将抛出异常。它适用于需要强制事务支持的方法。
当使用 MANDATORY 传播机制时,方法必须在一个已存在的事务中执行。如果当前没有事务,则会抛出异常。
下面是一个简单的示例示范如何使用 MANDATORY 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.MANDATORY)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 MANDATORY 传播机制。如果在调用该方法时已经存在一个事务,那么该方法将在该事务中运行。如果在调用该方法时没有事务,那么将会抛出异常。
假设在调用 performTransaction() 方法之前已经存在一个事务,那么在方法内部的数据库操作将在该事务中进行。如果任何一个数据库操作失败,事务将回滚,所有的改变将被撤销。
如果在调用 performTransaction() 方法之前没有事务,那么将会抛出异常,因为 MANDATORY传播机制要求方法必须在一个已存在的事务中执行。这样可以确保在调用该方法时,事务的一致性和完整性得到保证。
使用 MANDATORY 传播机制的好处是,可以确保方法在一个事务中执行,避免了在没有事务的情况下执行可能引起数据不一致的操作。这种传播机制通常用于业务操作中,确保相关操作在同一个事务中执行,保证数据的完整性。
3.4. REQUIRES_NEW
REQUIRES_NEW:每次执行时都会创建一个新的事务。
当使用 REQUIRES_NEW 传播机制时,方法将会在一个新的事务中执行。如果当前存在一个事务,则会挂起该事务,并在方法执行完毕后恢复原有事务。
下面是一个简单的示例示范如何使用 REQUIRES_NEW 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 REQUIRES_NEW 传播机制。无论当前是否存在一个事务,该方法都会在一个新的事务中执行。
假设在调用 performTransaction() 方法之前已经存在一个事务,那么在方法内部的数据库操作将在一个新的事务中进行。如果任何一个数据库操作失败,新的事务将回滚,所有的改变将被撤销,但原有的事务不受影响。
如果在调用 performTransaction() 方法之前没有事务,那么将会创建一个新的事务并在方法内部的数据库操作中使用。如果任何一个数据库操作失败,新的事务将回滚,所有的改变将被撤销。
使用 REQUIRES_NEW 传播机制的好处是,可以将方法的执行独立于当前的事务,确保方法在一个新的事务中执行。这种传播机制通常用于需要独立事务执行的情况,例如在进行一些敏感操作时,避免与现有事务发生冲突。同时,使用 REQUIRES_NEW 传播机制可以避免潜在的死锁问题,因为方法在一个新的事务中执行,不会与其他事务产生竞争。
3.5. NOT_SUPPORTED
NOT_SUPPORTED:方法将以非事务的方式运行,如果当前存在事务,则将其挂起。适用于不需要事务支持的方法。
当使用 NOT_SUPPORTED 传播机制时,方法将会在非事务的上下文中执行。如果当前存在一个事务,则会将事务挂起,并在方法执行完毕后恢复原有事务。
下面是一个简单的示例示范如何使用 NOT_SUPPORTED 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 NOT_SUPPORTED 传播机制。无论当前是否存在一个事务,该方法都会在非事务的上下文中执行。
假设在调用 performTransaction() 方法之前已经存在一个事务,那么在方法内部的数据库操作将在非事务的上下文中进行。如果任何一个数据库操作失败,将不会回滚,所有的改变将被提交。
如果在调用 performTransaction() 方法之前没有事务,那么方法将在非事务的上下文中执行,同样,如果任何一个数据库操作失败,将不会回滚,所有的改变将被提交。
使用 NOT_SUPPORTED 传播机制的好处是,方法将在非事务的上下文中执行,不受当前事务的影响。这种传播机制通常用于不需要事务支持的操作,例如查询操作或不涉及数据更新的方法。同时,使用 NOT_SUPPORTED 传播机制可以避免潜在的事务相关问题,例如死锁或锁竞争。然而,需要注意的是,使用 NOT_SUPPORTED 传播机制会导致方法无法回滚,因此在处理异常情况时需要特别小心。
3.6. NEVER
NEVER:有事物则报异常,它适用于不允许事务支持的方法。
当使用 NEVER 传播机制时,方法将会在非事务的上下文中执行。如果当前存在一个事务,则会抛出一个异常,阻止方法执行。
下面是一个简单的示例示范如何使用 NEVER 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.NEVER)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 NEVER 传播机制。如果在调用该方法之前已经存在一个事务,那么方法将会抛出一个异常,阻止方法的执行。
使用 NEVER 传播机制的好处是,可以确保方法在非事务的上下文中执行,避免了方法对事务的依赖。这种传播机制通常用于强制要求方法不在事务中执行的情况,例如某个方法需要独立于当前事务执行,或者需要确保方法不会影响当前事务的状态。
需要注意的是,使用 NEVER 传播机制会抛出异常,因此在方法调用时需要处理该异常。同时,使用 NEVER 传播机制也意味着方法无法回滚,因此在处理异常情况时需要特别小心。
3.7. NESTED
NESTED:如果之前存在一个事务,则创建一个嵌套事务。嵌套事务的回滚不会影响到父事务,但是父事务的回滚会影响到嵌套事务。
当使用 NESTED 传播机制时,方法将在一个新的嵌套事务中执行。如果当前不存在事务,则会创建一个新的事务。如果当前存在事务,则会在当前事务的上下文中创建一个嵌套事务。
下面是一个简单的示例示范如何使用 NESTED 传播机制:
@Service
public class TransactionService {
@Autowired
private TransactionDao transactionDao;
@Transactional(propagation = Propagation.NESTED)
public void performTransaction(String fromAccount, String toAccount, double amount) {
try {
// 执行转账操作
transactionDao.debit(fromAccount, amount);
transactionDao.credit(toAccount, amount);
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
在这个示例中,performTransaction() 方法使用 NESTED 传播机制。如果在调用该方法之前已经存在一个事务,那么方法将在当前事务的上下文中创建一个嵌套事务。如果在调用该方法之前不存在事务,则会创建一个新的事务。
NESTED 传播机制的特点是,嵌套事务是作为当前事务的一部分进行管理的。当嵌套事务提交时,它会将自己的修改合并到父事务中;当嵌套事务回滚时,它会回滚自己的修改,而不会影响父事务的状态。如果父事务提交,嵌套事务的提交也会被提交;如果父事务回滚,嵌套事务的回滚也会被回滚。
需要注意的是,NESTED 传播机制仅在特定的事务管理器中受支持,例如 Spring 的JtaTransactionManager。另外,嵌套事务的支持也取决于数据库的支持情况。
在使用 NESTED 传播机制时,需要考虑事务的管理和回滚策略。由于嵌套事务是作为当前事务的一部分进行管理的,因此在处理异常情况时需要特别小心,以确保事务的正确回滚。
四. 总结
事务传播机制允许开发者根据实际需求来控制事务的行为。通过合理选择事务传播机制,可以保证事务的一致性和可靠性,提高系统的稳定性和性能。
需要注意的是,事务传播机制只对方法级别的事务起作用,对注解方式的事务不适用。在使用注解方式时,可以通过 @Transactional 注解的 propagation 属性来指定事务的传播行为。