【Spring事务】

介绍

Spring 事务是对底层事务管理机制的封装和提供了更方便的编程模型,它实现了声明式事务管理和编程式事务管理两种方式。Spring 事务的核心是事务管理器(TransactionManager),Spring 支持多种事务管理器,如 DataSourceTransactionManager。

特点

可以灵活配置不同的事务传播行为
支持声明式事务管理和编程式事务管理
支持多种事务管理器,如JDBC
支持回滚和提交事务,并能够捕获事务异常

实现原理

Spring事务管理的实现原理是AOP(面向切面编程),通过在事务方法前后动态代理,在事务方法执行前开启事务,在事务方法执行后根据方法执行情况决定是提交事务还是回滚事务。

应用场景

1、业务操作需要保证一致性的场景,比如转账、订单操作等。
2、业务操作需要回滚的场景,比如商品库存不足时,需要回滚订单操作等。
3、多个业务操作需要在同一事务中执行的场景,比如订单提交、库存扣减、积分增加等。

声明式事务管理

介绍

声明式事务管理是一种通过配置文件或注解方式来实现事务管理的方法,它可以在不修改业务代码的情况下,将事务管理的控制权交给Spring容器来管理。

实现

通过AOP技术,在方法执行前后进行事务管理。

上代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class UserDAO {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public void addUser(User user) {
        userMapper.addUser(user);
    }
}

使用 @Transactional 注解将 addUser() 方法声明为一个事务方法。当这个方法被调用时,Spring 会自动创建一个事务,并在方法执行结束时根据方法执行的结果来决定事务的提交或回滚。

优点:

1、简化了事务管理的代码和操作:使用声明式事务,可以将事务管理的代码和业务逻辑分离,使得代码更加简洁明了;
2、方便配置:Spring 提供了多种方式来配置声明式事务,包括注解、XML 等,使用起来非常灵活;
3、支持事务的传播性:使用声明式事务,可以设置事务的传播性,即事务方法调用其他事务方法时,事务如何传播,这极大地增强了事务的灵活性和可控性;
4、支持事务的隔离级别:使用声明式事务,可以设置事务的隔离级别,保证事务并发执行时的数据安全性。

缺点:

1、不能满足特殊的业务需求:对于一些特殊的业务需求,例如需要在事务中使用底层数据库的特殊特性等,使用声明式事务可能会受到限制;
2、可能会影响系统的性能:事务管理需要对数据库进行加锁和解锁等操作,可能会对系统的性能产生一定的影响;
3、可能会增加代码复杂性:在配置和使用声明式事务时,可能需要了解和掌握一些 Spring 相关的知识,这可能会增加代码的复杂性。

编程式事务管理

编程式事务管理是通过编程方式来实现事务管理的方法,即在代码中显式地开启、提交、回滚事务等操作。

实现方式

通过TransactionTemplate或PlatformTransactionManager等类来实现事务管理。

上代码

1、TransactionTemplate是Spring事务管理的简化工具类,它封装了事务的开始、提交、回滚等操作,使得事务管理更加方便。使用TransactionTemplate时,可以通过execute()方法来执行需要进行事务管理的代码块。

@Autowired
    private TransactionTemplate transactionTemplate;

    @Override
    public void transfer(int fromUserId, int toUserId, int amount) {
        */
    /**
     * 使用了TransactionTemplate来实现事务管理,
     * TransactionTemplate封装了事务的启动、提交和回滚等操作,
     * 使用起来比较方便。
     * 

* 在transfer方法中,使用transactionTemplate.execute方法来执行事务回调操作 * ,回调中执行实际的业务逻辑,如果发生异常,则设置事务回滚并抛出RuntimeException。 *//* transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try { // 扣除转出用户的余额 userDao.decreaseBalance(fromUserId, amount); // 增加转入用户的余额 userDao.increaseBalance(toUserId, amount); } catch (Exception e) { status.setRollbackOnly(); throw new RuntimeException(e); } } }); }

2、PlatformTransactionManager是Spring事务管理的核心接口,它定义了事务管理的各种方法,例如获取、提交、回滚事务等。PlatformTransactionManager的实现类需要根据底层的数据访问技术来实现事务的管理。

@Autowired
    private PlatformTransactionManager transactionManager;

    /**
     * 使用PlatformTransactionManager来获取事务,然后执行实际的业务逻辑,如果发生异常,则手动回滚事务并抛出RuntimeException。
     * 需要注意的是,使用PlatformTransactionManager需要手动管理事务的启动、提交和回滚等操作,
     * 相对于TransactionTemplate来说,使用起来比较麻烦。
     * @param fromUserId
     * @param toUserId
     * @param amount
     */
    @Override
    public void transfer(int fromUserId, int toUserId, int amount) {
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(definition);
        try {
            // 扣除转出用户的余额
            userDao.decreaseBalance(fromUserId, amount);
            // 增加转入用户的余额
            userDao.increaseBalance(toUserId, amount);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw new RuntimeException(e);
        }
    }

优点

1、灵活性高,可以根据业务需要灵活配置事务传播行为和隔离级别。
2、可以更精细地控制事务,比如在代码中手动管理事务的提交和回滚。
3、可以更直观地了解事务的执行情况,比如可以在代码中打印日志等。

缺点

1、代码可读性差,需要显式地在代码中写入事务管理的相关代码,容易使代码变得冗长和难以维护。
2、可能会导致事务管理和业务逻辑的耦合度高,降低代码的可重用性和可维护性。

Spring事务传播行为有哪些?

PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务,否则创建一个新事务。
PROPAGATION_SUPPORTS:支持当前事务,如果当前不存在事务,则不开启事务。
PROPAGATION_MANDATORY:强制要求当前存在事务,如果当前不存在事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:创建一个新事务,如果当前存在事务,则将当前事务挂起。
PROPAGATION_NOT_SUPPORTED:不支持事务,如果当前存在事务,则将当前事务挂起。
PROPAGATION_NEVER:不支持事务,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在该事务内嵌套一个子事务,否则创建一个新事务。

应用场景

REQUIRED:如果当前存在事务,则加入该事务,否则新建一个事务。适用于核心事务,要求所有操作必须在一个事务中执行。

SUPPORTS:如果当前存在事务,则加入该事务,否则不创建事务。适用于只读操作,比如查询操作,如果不存在事务,就不需要创建事务。

MANDATORY:必须在一个已有的事务中执行,否则抛出异常。适用于核心操作,必须在一个已有的事务中执行。

REQUIRES_NEW:必须创建一个新事务,如果当前存在事务,则挂起该事务。适用于某些操作必须新开一个事务,比如敏感操作,需要单独事务处理。

NOT_SUPPORTED:不应该在事务中执行,如果当前存在事务,则挂起该事务。适用于只读操作,可以不受事务控制。

NEVER:不应该在事务中执行,如果当前存在事务,则抛出异常。适用于非核心操作,不需要事务保证。

NESTED:如果当前存在事务,则在嵌套事务内执行,否则新建一个事务。嵌套事务是基于保存点实现的,在内部事务中进行操作,只有内部事务都成功提交后,才能提交外部事务。适用于嵌套业务逻辑,需要独立的事务处理。

Spring事务隔离级别有哪些?

ISOLATION_DEFAULT:使用数据库的默认隔离级别。
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取未提交的数据,可能导致脏读、不可重复读和幻读。
ISOLATION_READ_COMMITTED:允许读取已提交的数据,避免了脏读,但可能出现不可重复读和幻读。
ISOLATION_REPEATABLE_READ:保证一个事务中多次读取同一数据的结果一致,避免了脏读和不可重复读,但可能出现幻读。
ISOLATION_SERIALIZABLE:最高的隔离级别,通过对数据加锁来避免脏读、不可重复读和幻读,但可能影响并发性能。

Spring事务管理中的传播行为和隔离级别的选择

传播行为:根据业务需求和事务边界进行选择,避免出现事务嵌套或事务无法生效的情况。例如,对于多个操作需要保证一致性的场景,应该使用PROPAGATION_REQUIRED传播行为。

隔离级别:根据数据的读写特性和并发访问情况进行选择,避免出现数据不一致或死锁等问题。例如,对于高并发读写的场景,应该使用ISOLATION_READ_COMMITTED或ISOLATION_REPEATABLE_READ隔离级别。

Spring事务回滚的方式有哪些?

默认情况下,只有RuntimeException及其子类才会触发事务回滚。
通过设置rollbackFor属性,可以指定触发事务回滚的异常类型。
通过设置noRollbackFor属性,可以指定不触发事务回滚的异常类型。

1、手动回滚事务:在事务方法中捕获异常并调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()方法。

@Transactional
    public void myTransactionalMethod() {
        try {
            // 执行业务操作
            // 如果发生异常,则手动回滚事务
            if (false) {
                throw new RuntimeException("业务异常");
            }
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw e;
        }
    }

2、自动回滚事务:当事务方法抛出未处理的异常时,Spring会自动回滚事务。

Spring会自动回滚事务的情况包括:
事务方法抛出了未处理的RuntimeException异常或其子类异常;

/**
 * 事务方法抛出了未处理的RuntimeException异常或其子类异常:
 */
@Service
@Transactional
public class UserService1 {
    
    @Autowired
    private UserRepository userRepository;
    
    public void addUser(User user) {
        // 如果插入用户失败,则抛出RuntimeException异常
        if (userRepository.insertUser(user) <= 0) {
            throw new RuntimeException("插入用户失败");
        }
    }
    
}

事务方法抛出了Error异常或其子类异常;

/**
 * 事务方法抛出了Error异常或其子类异常:
 */
@Service
@Transactional
public class UserService2 {
    
    @Autowired
    private UserRepository userRepository;
    
    public void addUser(User user) {
        // 如果插入用户失败,则抛出Error异常
        if (userRepository.insertUser(user) <= 0) {
            throw new OutOfMemoryError("插入用户失败");
        }
    }
    
}

当一个事务方法调用了一个被标记为@Transactional的方法,而被调用的方法抛出了未处理的RuntimeException异常或其子类异常时;

/**
 * 当一个事务方法调用了一个被标记为@Transactional的方法,而被调用的方法抛出了未处理的RuntimeException异常或其子类异常时:
 */
@Service
@Transactional
public class UserService3 {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void addUser(User user) {
        // 如果插入用户失败,则抛出RuntimeException异常
        if (userRepository.insertUser(user) <= 0) {
            throw new RuntimeException("插入用户失败");
        }
    }
    
}

当一个事务方法调用了一个被标记为@Transactional的方法,而被调用的方法抛出了Error异常或其子类异常时;

/**
 * 当一个事务方法调用了一个被标记为@Transactional的方法,而被调用的方法抛出了Error异常或其子类异常时:
 */
@Service
@Transactional
public class UserService4 {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void addUser(User user) {
        // 如果插入用户失败,则抛出Error异常
        if (userRepository.insertUser(user) <= 0) {
            throw new OutOfMemoryError("插入用户失败");
        }
    }
    
}

事务方法执行的时间超过了设定的超时时间。超时时间是指在事务执行过程中,如果事务处理时间超过了指定的时间,就会自动回滚事务。超时时间可以通过TransactionDefinition接口中的setTransactionTimeout()方法设置,默认值为-1,表示不设限制。

/**
 * 事务方法执行的时间超过了设定的超时时间:
 */
@Service
@Transactional(timeout = 1) // 设置超时时间为1秒
public class UserService5 {
    
    @Autowired
    private UserRepository userRepository;
    
    public void addUser(User user) {
        try {
            // 睡眠2秒,超过超时时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 如果插入用户失败,则抛出RuntimeException异常
        if (userRepository.insertUser(user) <= 0) {
            throw new RuntimeException("插入用户失败");
        }
    }
    
}

需要注意的是,如果事务方法抛出了已检查异常,比如IOException,Spring默认不会回滚事务。如果需要回滚事务,需要在异常类上加上@Transactional的rollbackFor属性来指定回滚的异常类型。

@Service
public class UserService6 {

    /**
     * 通过rollbackFor属性指定回滚的异常类型为IOException。
     * 在方法中,当捕获到IOException时,手动抛出异常,让Spring自动回滚事务。
     * @param filePath
     * @throws IOException
     */
    @Transactional(rollbackFor = IOException.class)
    public void readFromFile(String filePath) throws IOException {
        try (FileInputStream fileInputStream = new FileInputStream(filePath)){
            // 读取文件操作
        } catch (IOException e) {
            // 发生IOException,手动抛出异常,让Spring自动回滚事务
            throw e;
        }
    }
}

Spring事务管理和数据库事务的关系

Spring事务管理是对数据库事务的封装和扩展,它提供了一种统一的编程接口,让应用程序可以通过同一的方式来管理不同类型的事务,比如JDBC事务。Spring事务管理会将事务的控制委托给底层的数据库事务,具体实现方式取决于底层的数据访问技术。

Spring事务管理中的事务切面

事务切面是指通过AOP技术对需要进行事务管理的方法进行拦截,自动添加事务管理代码,实现对事务的自动管理。事务切面的优点是实现了事务管理与业务逻辑的分离,简化了代码实现,提高了代码的可维护性和可重用性。

你可能感兴趣的:(记录,框架,Java,spring,java,spring,boot)