Spring-事务

Spring-事务

1.事务基本概念

1.1. 什么是事务:

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

1.2. 事务特性ACID:

  1. 原子性(Atomicity):事务作为一个整体操作,要么全部执行成功,要么全部失败回滚,不允许部分执行成功部分失败。
  2. 一致性(Consistency):事务执行前后,数据库从一个一致状态转换为另一个一致状态。事务执行过程中,数据库的数据保持一致性约束。
  3. 隔离性(Isolation):事务的执行应该与其他事务隔离开来,每个事务在执行过程中看到的数据应该是一致的。事务隔离级别包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
  4. 持久性(Durability):一旦事务提交,对数据库的修改应该是永久性的,即使发生系统故障也不会丢失。

2. Spring事务管理概述

Spring框架通过对AOP(面向切面编程)的支持来实现事务管理。在Spring中,事务管理是通过在方法执行前后应用拦截器(即事务通知)来实现的。Spring事务管理是Spring框架提供的一种机制,用于管理应用程序中的事务操作。它提供了一种简单而强大的方式来处理数据库操作的事务性需求,并确保数据的一致性和完整性。

2.1. Spring事务的原理可以概括为以下几个关键点:

  1. 事务管理器(TransactionManager):事务管理器负责管理事务的生命周期,包括事务的开始、提交和回滚操作。Spring支持多种事务管理器,如JDBC事务管理器、Hibernate事务管理器、JTA事务管理器等。

  2. 事务切入点(TransactionInterceptor):事务切入点定义了哪些方法需要应用事务。通过配置切入点,可以将事务逻辑织入到目标方法中。

  3. 事务通知(TransactionAdvice):事务通知是指在方法执行前后,执行相关的事务操作。事务通知包括事务的开始、提交和回滚等操作。

  4. 事务属性(TransactionAttribute):事务属性定义了事务的行为,包括事务的隔离级别、传播行为、只读属性、超时设置等。通过事务属性,可以灵活地配置事务的特性。

  5. 事务管理器和事务通知的绑定:Spring框架将事务管理器和事务通知进行绑定,通过AOP代理技术,在方法执行前后应用事务通知,并委托给事务管理器来管理事务的生命周期。

2.2. 使用Spring事务管理的好处包括:

  1. 简化事务管理:通过声明式事务管理,可以将事务管理的逻辑从业务代码中分离出来,使业务代码更加清晰和简洁。
  2. 提供灵活性和可扩展性:通过配置不同的事务属性,可以灵活地控制事务的行为,满足不同场景下的需求。
  3. 支持多种数据访问技术:Spring事务管理可以与各种数据访问技术(如JDBC、Hibernate、JPA等)集成使用,提供统一的事务管理机制。

总的来说,Spring事务管理是一个强大而灵活的机制,能够帮助开发者实现可靠的事务处理,保证数据的一致性和完整性,并提供了简化和统一的事务管理方式。

2.3. Spring事务管理支持以下几种事务管理方式:

  1. 编程式事务管理:通过编写代码显式地控制事务的开始、提交或回滚。这种方式需要手动管理事务的边界,可以实现对事务的细粒度控制。

  2. 声明式事务管理:通过配置元数据(例如XML配置文件或注解)来定义事务的属性,Spring框架会根据这些配置自动管理事务的边界。这种方式更加方便和简洁,减少了代码的侵入性。

3. 编程式事务管理

一般不用,主要使用声明事务管理.

如果是分布式系统,使用分布式事务管理。

编程式事务管理是一种使用编程代码显式控制事务边界的方式。在Spring中,可以使用编程式事务管理来管理事务的开始、提交和回滚操作,以及设置事务的属性。

下面是使用编程式事务管理的基本步骤:

  1. 获取事务管理器(TransactionManager):首先,需要获取一个事务管理器的实例,它负责管理事务的开始、提交和回滚操作。可以通过Spring的依赖注入机制获取事务管理器的实例。
  2. 创建事务定义(TransactionDefinition):接下来,需要创建一个事务定义对象,用于定义事务的属性,包括事务的隔离级别、传播行为、超时时间等。可以通过编程方式创建一个事务定义对象。
  3. 开启事务:在需要开启事务的方法或代码块之前,调用事务管理器的beginTransaction()方法来开启事务。这将创建一个新的事务,并将其关联到当前的线程。
  4. 执行业务逻辑:在事务范围内,执行需要进行事务管理的业务逻辑代码。
  5. 判断事务结果并提交或回滚:根据业务逻辑的执行结果,决定是提交事务还是回滚事务。可以使用事务管理器的commit()方法提交事务,或者使用rollback()方法回滚事务。

下面是一个使用编程式事务管理的示例:

@Autowired
private PlatformTransactionManager transactionManager;

public void performTransaction() {
    // 获取事务定义
    DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

    // 开启事务
    TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

    try {
        // 执行业务逻辑
        // ...

        // 判断业务逻辑结果并提交事务
        if (businessLogicSuccessful) {
            transactionManager.commit(transactionStatus);
        } else {
            transactionManager.rollback(transactionStatus);
        }
    } catch (Exception e) {
        // 异常处理并回滚事务
        transactionManager.rollback(transactionStatus);
        throw e;
    }
}

编程式事务管理的优点是可以实现对事务的细粒度控制,可以根据需要在代码的任意位置开启、提交或回滚事务。然而,它的缺点是代码的侵入性较高,需要在每个需要事务管理的方法中手动管理事务的边界。因此,对于大型应用或复杂的事务逻辑,声明式事务管理更为常用和方便。

4. 声明式事务管理

声明式事务管理是一种通过配置的方式来管理事务的边界,而不需要在业务代码中显式地编写事务管理的代码。在Spring框架中,可以使用声明式事务管理来实现事务的开始、提交和回滚操作,以及设置事务的属性。

下面是使用声明式事务管理的基本步骤:

  1. 配置事务管理器(TransactionManager):首先,需要配置一个事务管理器的实例,它负责管理事务的开始、提交和回滚操作。可以通过Spring的配置文件或注解方式进行配置。
  2. 配置事务通知(Transaction Advice):接下来,需要配置一个事务通知,用于在方法执行前后进行事务的开启、提交和回滚操作。可以使用Spring的事务注解(如@Transactional)或基于XML的配置方式来配置事务通知。
  3. 配置事务切入点(Transaction Pointcut):事务切入点用于指定哪些方法需要应用事务管理。可以使用Spring的切入点表达式(如@Pointcut注解或XML配置)来定义事务切入点。
  4. 执行业务逻辑:在需要进行事务管理的方法中,执行业务逻辑代码。当方法被调用时,声明式事务管理会根据配置的事务通知和切入点进行事务的管理。

下面是一个使用声明式事务管理的示例:

@Transactional
public void performTransaction() {
    // 执行业务逻辑
    // ...
}

在上面的示例中,使用了@Transactional注解来标记方法,表示该方法需要进行事务管理。当方法被调用时,声明式事务管理会根据配置的事务通知和切入点,在方法执行前开启事务,在方法执行后根据方法的返回值决定是提交事务还是回滚事务。

声明式事务管理的优点是可以将事务管理与业务逻辑解耦,简化了代码的编写,提高了代码的可维护性和可读性。同时,它还提供了灵活的事务配置方式,可以通过注解或XML进行配置。然而,声明式事务管理的缺点是对于一些复杂的事务场景可能不够灵活,无法实现细粒度的事务控制。在这种情况下,可以结合使用编程式事务管理来满足特定需求。

总结来说,声明式事务管理适用于大多数简单的事务场景,可以通过简单的配置实现事务管理,提高开发效率和代码的可读性。对于复杂的事务场景,可以使用编程式事务管理或结合两种方式来满足需求。

4.1. 模拟场景:

用户购买图书,先查询图书的价格,再更新图书的库存和用户的余额

假设用户id为1的用户,购买id为1的图书

用户余额为50,而图书价格为80

购买图书之后,用户的余额为-30,数据库中余额字段设置了无符号,因此无法将-30插入到余额字段

此时执行sql语句会抛出SQLException

4.2. 准备工作:

1)涉及环境:

JDK17
Spring6

MySQL8

2)涉及pom:

<dependencies>
    <!--spring context依赖-->
    <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!--spring对junit的支持相关依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.1</version>
    </dependency>
    <!--spring jdbc  Spring 持久化层支持jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    <!-- druid数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.15</version>
    </dependency>
    <!--log4j2的依赖-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.19.0</version>
    </dependency>
</dependencies>

3)数据库表:

CREATE DATABASE `spring6`;

use `spring6`;

CREATE TABLE `t_book` (
  `book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `book_name` varchar(20) DEFAULT NULL COMMENT '图书名称',
  `price` int(11) DEFAULT NULL COMMENT '价格',
  `stock` int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)',
  PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert  into `t_book`(`book_id`,`book_name`,`price`,`stock`) values (1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);

CREATE TABLE `t_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(20) DEFAULT NULL COMMENT '用户名',
  `balance` int(10) unsigned DEFAULT NULL COMMENT '余额(无符号)',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert  into `t_user`(`user_id`,`username`,`balance`) values (1,'admin',50);

4.3. 配置类:

扫描组件:包路径换成自己项目

MySQL:换成自己数据库配置

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;

@Configuration
@ComponentScan("com.xxx")
@EnableTransactionManagement
public class SpringConfig {

    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

4.4. 事务使用:

BookController

@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    public void buyBook(Integer bookId, Integer userId){
        bookService.buyBook(bookId, userId);
    }
}

BookService

public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

BookServiceImpl

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }
}

BookDao

public interface BookDao {
    Integer getPriceByBookId(Integer bookId);

    void updateStock(Integer bookId);

    void updateBalance(Integer userId, Integer price);
}

BookDaoImpl

@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Integer getPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
    }

    @Override
    public void updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        jdbcTemplate.update(sql, bookId);
    }

    @Override
    public void updateBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        jdbcTemplate.update(sql, price, userId);
    }
}

4.5. 测试:

public class TxByAllAnnotationTest {

    @Test
    public void testTxAllAnnotation(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookController accountService = applicationContext.getBean("bookController", BookController.class);
        accountService.buyBook(1, 1);
    }
}

4.6. 结果:

1)操作前数据库表数据:

用户admin余额50、斗破库存100、斗罗库存100。

2)没有加@Transactional注解:

在这里插入图片描述

用户admin,买斗破苍穹和斗罗时,由于在买斗罗大陆时由于数据库字符约束unsigned限制,导致数据异常。用户余额还是50,斗破苍穹库存为99,斗罗大陆库存100,凭空少了一本斗破。

在这里插入图片描述

3)加了@Transactional注解:

在buyBook方法上加了@Transactional注解

@Transactional
@Override
public void buyBook(Integer bookId, Integer userId) {
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
}

用户admin,买斗破苍穹和斗罗时,由于在买斗罗大陆时由于数据库字符约束unsigned限制,导致数据异常。事务回滚,用户余额还是50,斗破苍穹库存为100,斗罗大陆库存100。

在这里插入图片描述

5. Spring事务管理的属性配置

5.1. 只读

指定事务是否为只读事务。如果只读事务中没有写操作,可以提高并发性能。

@Transactional(readOnly = true)

对增删改操作设置只读会抛出下面异常:

Caused by: java.sql.SQLException: Connection is read-only. 
Queries leading to data modification are not allowed

5.2. 超时

指定事务的最大执行时间,超过该时间将自动回滚事务。

//超时时间单位秒
@Transactional(timeout = 3)

执行过程中抛出异常:

org.springframework.transaction.TransactionTimedOutException: 
Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022

5.3. 回滚策略

用于定义事务回滚的规则。可以指定特定的异常类型,当出现该异常时进行事务回滚。

可以通过@Transactional中相关属性设置回滚策略

  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
  • noRollbackFor属性:需要设置一个Class类型的对象
  • rollbackFor属性:需要设置一个字符串类型的全类名
@Transactional(noRollbackFor = ArithmeticException.class)

5.4. 隔离级别

指定事务的隔离级别,用于控制并发事务之间的互相影响。

隔离级别一共有四种:

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化

各个隔离级别解决并发问题的能力:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

5.5. 传播行为

定义事务在方法调用链中的传播方式。常用的传播行为有REQUIRED、REQUIRES_NEW等。

例如,当一个方法调用另一个方法时,如何处理事务边界。在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。

一共有七种传播行为:

  • REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行**【有就加入,没有就不管了】**
  • MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常**【有就加入,没有就抛异常】**
  • REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起**【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】**
  • NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务**【不支持事务,存在就挂起】**
  • NEVER:以非事务方式运行,如果有事务存在,抛出异常**【不支持事务,存在就抛异常】**
  • NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
// 默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。
@Transactional(propagation = Propagation.REQUIRED)

// 表示不管当前线程上是否有已经开启的事务,都要开启新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)

6. Spring事务管理的最佳实践

在使用Spring进行事务管理时,以下是一些最佳实践:

  1. 尽量将事务控制范围缩小:只在必要的方法或方法调用链上添加事务注解,避免过大的事务范围。这样可以减小锁的粒度,提高并发性能。
  2. 使用声明式事务管理:优先使用声明式事务管理,通过@Transactional注解或XML配置来定义事务属性。这样可以将事务的配置与业务逻辑分离,使代码更加清晰。
  3. 考虑事务的隔离级别:根据业务需求和性能要求选择合适的事务隔离级别。通常情况下,使用默认的隔离级别(READ_COMMITTED)即可满足大多数需求。如果有特殊需求,可以选择其他级别。
  4. 设置合理的事务超时时间:根据业务逻辑和性能考虑,设置合理的事务超时时间。如果事务执行时间过长,可以考虑调优或优化相关操作。
  5. 捕获和处理异常:在事务方法中捕获和处理异常,避免事务回滚后无法正确处理异常情况。可以根据实际业务需求,选择回滚或手动处理异常。
  6. 选择合适的事务传播行为:根据方法调用链的需求,选择合适的事务传播行为。例如,如果需要在当前事务内部调用另一个方法,可以选择REQUIRED传播行为。
  7. 使用只读事务:对于只读操作,可以使用只读事务,可以提高并发性能。只读事务不会对数据进行修改,因此可以减少锁竞争。
  8. 监控和调优:监控事务的性能和运行情况,根据需要进行调优。可以使用Spring提供的事务管理相关的统计信息和监控工具。
  9. 使用合适的事务管理器:根据具体的数据访问技术和需求,选择合适的事务管理器。例如,对于JDBC事务管理,可以使用DataSourceTransactionManager
  10. 进行单元测试:针对事务的业务逻辑编写单元测试,确保事务的正确性和稳定性。

综上所述,通过遵循这些最佳实践,可以更好地使用Spring进行事务管理,确保事务的一致性、隔离性和可靠性。同时,也可以根据具体业务需求进行适当的调整和优化。

7. 事务失效场景

事务失效是指在使用事务管理时,事务的一些特性无法正常工作,导致事务不起作用或无法达到预期的结果。以下是一些常见的事务失效场景:

  1. 方法没有被正确标注为事务:在使用声明式事务管理时,可能会忘记在方法上添加@Transactional注解或XML配置,导致事务管理器无法识别和管理该方法的事务。
  2. 异常未被捕获或处理:当事务方法中的异常未被捕获或处理时,事务可能会被回滚,但异常信息没有被正确处理,导致事务失效。
  3. 外部方法调用内部事务方法:当一个事务方法内部调用另一个事务方法时,默认情况下,内部方法将在同一个事务中执行。但如果外部方法没有被标记为事务,或者内部方法的传播行为设置不正确,可能导致内部方法的事务失效。
  4. 不支持的数据库操作:某些数据库操作不支持事务,例如DDL语句(如创建表、修改表结构等)。在执行这些操作时,事务管理器无法控制事务的提交和回滚,导致事务失效。
  5. 事务超时:如果事务执行时间过长,超过了事务管理器设置的超时时间,事务可能会被自动回滚,导致事务失效。
  6. 多线程环境下的事务:在多线程环境下,如果事务方法中使用了共享变量或资源,并且没有正确处理线程安全性,可能导致事务失效或数据不一致。
  7. 多个数据源的事务:在使用多个数据源时,如果事务管理器没有正确配置或选择合适的事务管理策略,可能导致事务失效或数据不一致。

为了避免事务失效,应该仔细理解事务的特性和工作原理,并根据具体场景和需求正确配置和使用事务管理器。同时,编写单元测试和进行全面的测试,以确保事务在各种情况下都能正常工作。

8. 总结

事务是数据库操作中的重要概念,它可以确保一组数据库操作要么全部成功提交,要么全部回滚到初始状态。事务的目的是保证数据的一致性、完整性和可靠性。

以下是事务的一些关键点和总结:

  1. 事务的特性(ACID):事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)这四个特性。原子性表示事务要么全部成功,要么全部回滚;一致性表示事务执行前后数据库的状态保持一致;隔离性表示事务之间相互隔离,互不干扰;持久性表示一旦事务提交,对数据库的修改将永久保存。
  2. 事务的隔离级别:数据库提供了多个隔离级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。不同的隔离级别提供了不同的事务隔离程度,可以根据具体需求选择合适的隔离级别。
  3. 事务管理的方式:事务可以通过编程式事务管理和声明式事务管理两种方式来管理。编程式事务管理是在代码中显式地开启、提交和回滚事务;声明式事务管理是通过配置或注解来定义事务的属性和行为,由事务管理器自动管理事务。
  4. 事务的传播行为:在使用声明式事务管理时,可以设置事务的传播行为来控制事务的边界和范围。常见的传播行为包括REQUIRED、REQUIRES_NEW、NESTED等,每种传播行为都有不同的事务处理方式。
  5. 事务的失效和注意事项:事务可能会因为一些原因失效,导致事务不起作用或产生意外的结果。在使用事务时,需要注意异常处理、事务边界的设置、多线程环境下的事务管理等方面,以避免事务失效。
  6. 分布式事务:在分布式系统中,涉及多个数据库或服务的事务称为分布式事务。分布式事务的管理比较复杂,常见的解决方案包括两阶段提交(2PC)、补偿事务(TCC)和消息队列等。

总而言之,事务是保证数据库操作的一致性和可靠性的重要机制。正确理解和使用事务,结合合适的事务管理方式和隔离级别,可以提供可靠的数据操作和高效的应用程序。

9. 参考

  • 尚硅谷-Spring6
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值