在 Spring 中使用 @Transactional 时要避免的常见错误

在 Spring Framework 的世界中,使用 管理事务是一项强大的功能。然而,权力越大,责任越大。对此注释的误解和误用可能会导致细微的错误和数据不一致。今天,我们将揭示一些常见的陷阱以及如何避免它们。@Transactional

Spring 应用程序中的一个常见疏忽是在私有方法上使用注解或从同一个 Bean 中调用它,这会导致注解被静默忽略。@Transactional

在深入研究下一个常见错误之前,让我们先来看看一个典型的实际示例,如Spring应用程序中的这个实现所示。@TransactionalAccountService

Account.java

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "account")
public class Account{

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String ownerName;
  private BigDecimal balance;

}

AccountService.java

@Service
public class AccountService {

  @Autowired private AccountRepository accountRepository;

  @Transactional
  public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    try {

      Account fromAccount =
          accountRepository
              .findById(fromAccountId)
              .orElseThrow(
                  () -> new Exception("Account not found: " + fromAccountId));

      Account toAccount =
          accountRepository
              .findById(toAccountId)
              .orElseThrow(() -> new Exception("Account not found: " + toAccountId));

      if (fromAccount.getBalance().compareTo(amount) < 0) {
        throw new Exception("Insufficient balance in account: " + fromAccountId);
      }

      fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
      accountRepository.save(fromAccount);

      // Simulate an error in updating the recipient's account
      if (toAccount.getId().equals((long) -1)) {
        throw new Exception("Error in processing recipient account");
      }

      toAccount.setBalance(toAccount.getBalance().add(amount));
      accountRepository.save(toAccount);
    } catch (Exception e) {
      log.error("Error occurred while transferring money ...");
    }
  }
}

下面是一个过于简化的示例,说明了@Transactional是如何运作的:

import java.sql.Connection;
import java.sql.SQLException;

public class TransactionExample {

    public void executeTransaction() {
        // Get a database connection
        Connection connection = dataSource.getConnection();

        try (connection) {
            // Begin the transaction
            connection.setAutoCommit(false);

            // Execute SQL statements here
            // Example: INSERT, UPDATE, or DELETE operations

            // Commit the transaction
            connection.commit();
        } catch (SQLException e) {
            // Roll back the transaction in case of an error
            connection.rollback();
        }
    }
}

注解标记 Spring 应用程序中事务的开始和结束。当方法用 @Transactiona时,事务在方法开始后立即开始,并在方法完成时结束。这样,在方法中完成的所有操作要么协同工作,要么根本不发生,从而保持数据的安全性和一致性。

有了这个背景,我们现在就可以深入研究常见的错误

在方法中捕获异常@Transactiona

Spring 中事务管理的一个经常被误解的关键方面涉及处理用 .在此类方法中捕获和处理异常时,可能会无意中阻止事务回滚,从而可能导致意外的数据状态。@Transactional

现在让我们修改上面的代码以抛出错误,以便我们可以回滚

@Service
public class AccountService {

  @Autowired private AccountRepository accountRepository;

  @Transactional
  public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) throws Exception {
    try {
      // ... fetch accounts

      fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
      accountRepository.save(fromAccount);

      // Simulate an error in updating the recipient's account
      if (toAccount.getId().equals((long) -1)) {
        throw new Exception("Error in processing recipient account");
      }

      toAccount.setBalance(toAccount.getBalance().add(amount));
      accountRepository.save(toAccount);
    } catch (Exception e) {
      log.error("Error occurred while transferring money ...");
      throw e;
    }
  }
}

s但是等等,有一个问题:这仍然不会使事务回滚,因为默认情况下,“事务将在

RuntimeException 和 Error 上回滚,但不会在选中的异常时回滚。

若要覆盖此默认行为,我们必须使用要回滚的异常类设置属性。现在,该函数将如下所示:rollbackFor

@Transactional(rollbackFor = Exception.class)
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) throws Exception {
  try {
    // ... transaction fails ...
  } catch (Exception e) {
    log.error("Error occurred while transferring money ...");
    throw e;
  }
}

请记住,在 Spring 中正确使用涉及两个关键实践:确保将其应用于公共方法或外部 Bean 调用以避免被忽略,以及设置属性以进行精确的事务回滚控制。掌握这些方面对于可靠的事务管理和维护应用程序中的数据完整性至关重要。@TransactionalrollbackFo

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值