返回

实战演练 | 如何通过声明式与编程式事务确保数据安全

后端

数据关联性:业务场景中事务原子性的基石

数据问题:关联性是关键

数据是与事物息息相关的,当数据与事物之间的关联性出现问题时,后果可能非常严重。特别是在业务场景中,事务操作经常涉及多个资源,要求这些操作具有原子性,即要么都成功,要么都失败。

事务的本质:确保数据一致性和可靠性

如果没有事务机制,就会面临两个问题:一致性问题,不同资源之间的状态不一致;可靠性问题,中间某个操作失败,导致整个操作失败。例如,转账涉及两个账户,从一个账户扣款,给另一个账户加款,如果转账失败,可能会导致一个账户扣款成功,但另一个账户未加款,造成状态不一致。

因此,事务机制应运而生,其本质就是确保不同资源之间状态的一致性,保证数据的可靠性。数据库中的事务通常分为两种类型:声明式事务和编程式事务。

声明式事务:简单灵活

声明式事务顾名思义,通过声明的方式告诉系统需要进行一个事务操作。例如,在 Spring 中,可以使用 @Transactional 注解声明一个事务方法。

@Service
public class UserService {

    @Transactional
    public void transfer(int fromUserId, int toUserId, int amount) {
        // 从 fromUserId 账户扣款
        accountService.debit(fromUserId, amount);
        // 给 toUserId 账户加款
        accountService.credit(toUserId, amount);
    }
}

添加 @Transactional 注解后,Spring 会在方法执行期间自动开启一个事务,并在方法执行完成后自动提交或回滚事务。

编程式事务:完全控制

编程式事务通过编程的方式控制事务,例如在 Spring 中,可以使用 PlatformTransactionManagerTransactionStatus 来控制事务。

@Service
public class UserService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void transfer(int fromUserId, int toUserId, int amount) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // 从 fromUserId 账户扣款
            accountService.debit(fromUserId, amount);
            // 给 toUserId 账户加款
            accountService.credit(toUserId, amount);

            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
        }
    }
}

编程式事务提供了对事务的完全控制权,可以决定何时开启事务,何时提交或回滚事务。

选择事务机制

声明式事务使用起来更简单,但灵活性较差,而编程式事务使用起来更复杂,但灵活性更强。在实际应用中,可以根据需要选择使用哪种事务机制。

常见问题解答

  1. 什么是数据关联性?

    • 数据关联性是指数据与事物之间的联系,当关联性出现问题时,可能会导致严重后果。
  2. 事务的原子性是什么意思?

    • 事务的原子性是指要么所有操作都成功,要么所有操作都失败。
  3. 声明式事务和编程式事务有什么区别?

    • 声明式事务通过声明的方式开启事务,而编程式事务通过编程的方式控制事务。
  4. 哪种事务机制更好?

    • 选择事务机制取决于实际需要,声明式事务更简单,编程式事务更灵活。
  5. 事务在保证数据一致性和可靠性方面发挥着什么作用?

    • 事务通过确保不同资源之间状态的一致性,保证中间操作失败不会影响整个操作的完整性,从而保证数据一致性和可靠性。