SpringBoot 注解型事务管理

1、什么是@Transactional?
我们可以使用@Transactional 在数据库事务中包装一个方法。
它允许我们为事务设置传播,隔离,超时,只读和回滚条件。另外,我们可以指定事务管理器。
2、如何使用@Transactional
我们可以将注释放在接口,类的定义上,也可以直接放在方法上。它们根据优先级顺序相互覆盖。从最低到最高,我们有:接口,超类,类,接口方法,超类方法和类方法。
Spring将类级别的注释应用于我们未使用@Transactional注释的此类的所有公共方法
但是,如果将批注放在私有或受保护的方法上,Spring将忽略它而不会出现错误。
让我们从一个接口示例开始:
@Transactional
public interface TransferService {
   void transfer(String user1, String user2, double val);
}

通常,不建议在接口上设置@Transactional。但是,对于带有Spring Data的@Repository这样的情况,这是可以接受的。

我们可以将注释放在类定义上,以覆盖接口/超类的事务设置:

@Service
@Transactional
public class TransferServiceImpl implements TransferService {
  @Override
  public void transfer(String user1, String user2, double val) {
     // ... 
  }
}

现在,通过直接在方法上设置注释来覆盖它:

@Transactional
public void transfer(String user1, String user2, double val) {
  // ...
}

3.事务传播

传播定义了业务逻辑的事务边界。Spring根据我们的传播设置设法开始和暂停事务
Spring会根据传播调用TransactionManager :: getTransaction来获取或创建一个事务。它支持所有类型的TransactionManager的某些传播,但其中的一些传播仅受TransactionManager的特定实现支持
现在,让我们看一下不同的传播方式以及它们是如何工作的。

3.1. REQUIRED Propagation

默认传播为“必需”。Spring检查是否有活动的事务,如果不存在则创建一个新的事务。否则,业务逻辑将追加到当前活动的事务中
@Transactional(propagation = Propagation.REQUIRED)
public void requiredExample(String user) {
  // ...
}

另外,由于默认传播REQUIRED,因此我们可以通过删除代码来简化代码:

@Transactional
public void requiredExample(String user) {
  // ...
}

让我们来看看交易创造是如何工作的伪代码REQUIRED传播:

if (isExistingTransaction()) {
   if (isValidateExistingTransaction()) {
      validateExisitingAndThrowExceptionIfNotValid();
   }
   return existing;
}
return createNewTransaction();

3.2. SUPPORTS Propagation

对于SUPPORTS,Spring首先检查是否存在活动事务。如果存在事务,则将使用现有事务。如果没有事务,则以非事务方式执行:
@Transactional(propagation = Propagation.SUPPORTS)
public void supportsExample(String user) {
  // ...
}

让我们看看SUPPORTS的事务创建的伪代码

if (isExistingTransaction()) {
   if (isValidateExistingTransaction()) {
      validateExisitingAndThrowExceptionIfNotValid();
   }
   return existing;
}
return emptyTransaction;
3.3. MANDATORY Propagation
当传播为MANDATORY时,如果存在活动事务,则将使用该传播如果没有活动的事务,那么Spring会抛出一个异常:
@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryExample(String user) {
   // ...
}

再看看伪代码

if (isExistingTransaction()) {
   if (isValidateExistingTransaction()) {
      validateExisitingAndThrowExceptionIfNotValid();
   }
   return existing;
}
throw IllegalTransactionStateException;

3.4. NEVER Propagation

对于从不传播的事务逻辑,如果存在活动事务,Spring会引发异常:
@Transactional(propagation = Propagation.NEVER)
public void neverExample(String user) {
  // ...
}

让我们看一下伪造代码,它说明事务创建如何从不传播:

if (isExistingTransaction()) {
   throw IllegalTransactionStateException;
}
return emptyTransaction;

3.5. NOT_SUPPORTED Propagation

Spring首先挂起当前事务(如果存在),然后执行业务逻辑而没有事务。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedExample(String user) {
  // ...
}

JtaTransactionManager中支持实时交易暂停外的开箱。其他人则通过持有对现有引用的引用,然后从线程上下文中将其清除来模拟该悬浮。

3.6. REQUIRES_NEW Propagation

当传播为REQUIRES_NEW时,Spring暂停当前事务(如果存在),然后创建一个新事务:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewExample(String user) {
  // ...
}

NOT_SUPPORTED相似,我们需要JTATransactionManager进行实际的事务挂起。

伪代码如下所示:
if (isExistingTransaction()) {
   suspend(existing);
   try {
      return createNewTransaction();
   } catch (exception) {
      resumeAfterBeginException();
      throw exception;
   }
}
return createNewTransaction();

3.7. NESTED Propagation

对于NESTED传播,Spring检查事务是否存在,如果存在,则标记为保存点。这意味着,如果我们的业务逻辑执行抛出异常,则事务回滚到该保存点。如果没有活动的事务,则它的工作方式类似于REQUIRED
DataSourceTransactionManager开箱即用地支持这种传播。此外,JTATransactionManager的某些实现可能支持此功能。
JpaTransactionManager仅对JDBC连接支持NESTED但是,如果我们将nestedTransactionAllowed标志设置true,那么如果我们的JDBC驱动程序支持保存点,那么它也适用于JPA事务中的JDBC访问代码。
最后,让我们将传播设置NESTED
@Transactional(propagation = Propagation.NESTED)
public void nestedExample(String user) {
   // ...
}

4. Transaction Isolation

隔离是常见的ACID属性之一:原子性,一致性,隔离性和持久性隔离描述了并发事务所应用的更改如何彼此可见。
每个隔离级别可防止对事务进行零个或多个并发副作用:
脏读:读取并发事务的未提交更改
不可重复读取:如果并发事务更新同一行并提交,则在重新读取一行时获得不同的值
幻象读取:如果另一个事务在范围中添加或删除了一些行并提交,则在重新执行范围查询后获得不同的行
我们可以通过@Transactional :: isolation设置事务的隔离级别在春季它具有以下五个枚举:DEFAULTREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE。

4.1. Isolation Management in Spring

默认的隔离级别为DEFAULT因此,当Spring创建一个新事务时,隔离级别将是我们RDBMS的默认隔离。因此,如果我们更改数据库,我们应该小心。
当我们调用具有不同隔离度的方法链时,还应考虑各种情况在正常流程中,隔离仅在创建新事务时适用。因此,如果由于某种原因我们不想让方法以不同的隔离方式执行,则必须将TransactionManager :: setValidateExistingTransaction设置为true。然后,交易验证的伪代码将为:
if (isolationLevel != ISOLATION_DEFAULT) {
   if (currentTransactionIsolationLevel() != isolationLevel) {
      throw IllegalTransactionStateException
   }
}

现在让我们深入了解不同的隔离级别及其影响。

4.2. READ_UNCOMMITTED Isolation

READ_UNCOMMITTED 是最低的隔离级别,并允许大多数并发访问。
结果,它遭受了所提到的所有三个并发副作用。因此,具有这种隔离的事务将读取其他并发事务的未提交数据。同样,不可重复读取和幻像读取都可能发生。因此,在重新读取行或重新执行范围查询时,我们可以获得不同的结果。
我们可以为方法或类设置隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void log(String message) {
   // ...
}

Postgres不支持READ_UNCOMMITTED隔离,而是回退到READ_COMMITED另外,Oracle不支持并允许READ_UNCOMMITTED

4.3. READ_COMMITTED Isolation

第二个隔离级别READ_COMMITTED防止脏读。
其余的并发副作用仍然可能发生。因此,并发事务中未提交的更改对我们没有影响,但是,如果事务提交了更改,则可以通过重新查询来更改我们的结果。
在这里,我们设置隔离级别:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void log(String message){
  // ...
}

READ_COMMITTED是Postgres,SQL Server和Oracle的默认级别。

4.4. REPEATABLE_READ Isolation

第三个隔离级别REPEATABLE_READ防止脏的和不可重复的读取。因此,我们不受并发事务中未提交的更改的影响。
同样,当我们重新查询一行时,我们不会得到不同的结果。但是在重新执行范围查询时,我们可能会获得新添加或删除的行。
而且,它是防止丢失更新所需的最低级别。当两个或多个并发事务读取并更新同一行时,将发生丢失的更新。REPEATABLE_READ根本不允许同时访问一行。因此,丢失的更新不会发生。
以下是设置方法的隔离级别的方法:
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void log(String message){
   // ...
}

REPEATABLE_READ是Mysql中的默认级别。Oracle不支持REPEATABLE_READ

4.5. SERIALIZABLE Isolation

SERIALIZABLE是最高级别的隔离。它可以防止所有提到的并发副作用,但是由于它顺序执行并发调用,因此可以导致最低的并发访问率。
换句话说,并发执行一组可序列化事务具有与串行执行它们相同的结果。
现在,让我们看看如何将SERIALIZABLE设置隔离级别:
@Transactional(isolation = Isolation.SERIALIZABLE)
public void log(String message){
   // ...
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值