Spring事务详解

本文详细介绍了事务的四大特性,Spring事务的编程式和声明式管理,以及数据库如何处理事务。讨论了事务隔离级别、超时时间和传播行为,并给出了Spring在中小项目和大型项目中的处理方案示例,包括SpringBoot项目的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Spring中,事务是一系列的数据操作,要么全部执行完成,要么都不执行。这确保了数据的一致性和完整性。

1、事务的四大特性

事务的主要特性包括 原子性、一致性、隔离性和持久性。

  • 原子性 指的是事务是一个原子操作,由一系列动作组成,这些动作要么全部完成,要么完全不起作用。
  • 一致性 确保一旦事务完成(无论成功还是失败),系统必须处于一致的状态。
  • 隔离性 指的是事务之间应该隔离开来,因为可能有许多事务会同时处理相同的数据,每个事务都应该与其他事务有隔离策略。
  • 持久性 则表示一旦事务完成,其结果将不会受到影响,通常会被写到持久化存储器中。

2、Spring事务的实现方式

在Spring中,事务管理可以通过 编程式事务管理声明式事务管理 两种方式实现。

  • 编程式事务管理需要显式执行事务,如使用TransactionTemplate,需要显式调用commit或rollback方法。
  • 而声明式事务管理建立在AOP(面向切面编程)之上,通过AOP功能对方法前后进行拦截,将事务处理的功能编织到拦截的方法中。这种方式无需在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过注解(如@Transactional)来管理事务。

当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。这个时候就需要使用事务。

3、数据库怎么处理事务

  • jdbc访问数据库,处理事务, Connection connconn.commit()conn.rollback()
  • mybatis访问数据库,处理事务, SqlSession.commit()SqlSession.rollback()
  • hibernate访问数据库,处理事务, Session.commit()Session.rollback()

上述事务的处理方式,有什么不足?

  • 不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理。
  • 掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
  • 处理事务的使用多种方法。

总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

怎么解决不足?

  • Spring提供一种处理事务的统一模型,能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
  • 使用Spring的事务处理机制,可以完成mybatis访问数据库的事务处理。
  • 使用Spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

4、处理事务的方式

Spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给Spring就可以了。

事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback。事务管理器是一个接口和他的众多实现类。

  • 接口:PlatformTransactionManager `,定义了事务重要方法 commit ,rollback。
  • 实现类:Spring把每一种数据库访问技术对应的事务处理类都创建好了。
  • MyBatis访问数据库是 DataSourceTransactionManager
  • Hibernate访问数据库是 HibernateTransactionManager

你需要告诉Spring 你用是那种数据库的访问技术,需要声明数据库访问技术对于的事务管理器实现类, 在Spring的配置文件中使用 <bean> 声明就可以了。

例如,你要使用MyBatis访问数据库,你应该在xml配置文件中配置:

<bean id=“xxx" class="...DataSourceTransactionManager"> 

提交事务和回滚事务的时机:

  • 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,Spring在方法执行后提交事务。调用事务管理器commit 。
  • 当你的业务方法抛出运行时异常或ERROR,Spring执行回滚事务,调用事务管理器的rollback。
  • 运行时异常的定义:RuntimeException 和他的子类都是运行时异常,如 NullPointExceptionNumberFormatException
  • 当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务。
  • 受查异常:在你写代码中,必须处理的异常。例如IOExceptionSQLException

4、事务的隔离级别

你的业务方法需要什么样的事务,说明需要事务的类型。

事务的隔离级别:有4个值,默认为常量值,都是以 ISOLATION 开头的。

  • READ_UNCOMMITTED :读未提交。未解决任何并发问题。
  • READ_COMMITTED :读已提交。解决脏读,存在不可重复读与幻读。
  • REPEATABLE_READ :可重复读。解决脏读、不可重复读,存在幻读。
  • SERIALIZABLE :串行化。不存在并发问题。

DEFAULT :采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ ,Oracle默认为 READ_COMMITTED

5、事务的超时时间

Spring事务的超时时间是指一个事务在执行过程中允许的最长持续时间。如果事务在设定的超时时间内未能完成,则会自动回滚,以确保数据的一致性和完整性。

在Spring中,事务超时时间可以通过以下两种方式进行设置:

  • 使用 @Transactional 注解的 timeout 属性:在需要使用事务的方法上添加 @Transactional 注解,并通过 timeout 属性指定超时时间,单位为秒。例如,@Transactional(timeout = 10) 表示事务超时时间为10秒。
  • 使用编程式事务管理:通过 TransactionTemplate 类手动管理事务,并通过 setTimeout 方法设置超时时间,单位同样为秒。

事务超时时间的设置应根据实际情况进行权衡,过短的超时时间可能导致事务频繁回滚,而过长的超时时间则可能使得系统在等待长时间运行的事务时变得不响应。

需要明确的是,事务超时时间 指的是 数据库执行事务的最长允许时间 ,而不是Java程序执行的时间。因此,在设置事务超时时间时,应考虑到数据库操作的实际情况和性能特点。

6、事务的传播行为

Spring事务传播行为是指当一个事务方法(当前事务)被另一个事务方法(调用者)调用时,这个事务方法(当前事务)对另一个事务方法(调用者)的态度。简单来说,就是当一个事务方法A调用了另一个事务方法B时,B应该如何应对。

Spring框架提供了七种主要的事务传播行为,每种行为都有其特定的用途和适用场景:

  • PROPAGATION_REQUIRED :指定的方法必须在事务内执行。如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务。这是最常见的选择。
  • PROPAGATION_SUPPORTS :支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY :支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW :创建新事务,无论当前是否存在事务,都创建新事务。
  • PROPAGATION_NOT_SUPPORTED :以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER :以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED :如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED 类似的操作。

在实际开发中,应根据具体的业务逻辑和事务要求来选择合适的传播行为。同时,还需要注意事务的传播行为与其他事务属性(如隔离级别、只读标志等)的配合使用,以确保事务的正确性和性能。

7、Spring事务处理方案

1、中小项目使用

Spring框架自己用AOP实现给业务方法增加事务的功能, 使用 @Transactional 注解增加事务。

@Transactional 注解是Spring框架自己注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等

使用@Transactional的步骤:

  • 需要声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
  • 开启事务注解驱动, 告诉Spring框架,我要使用注解的方式管理事务。Spring使用AOP机制,创建 @Transactional 所在的类代理对象,给方法加入事务的功能。Spring给业务方法加入事务,在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用AOP的环绕通知。
@Around("你要增加的事务功能的业务方法名称")
public Object myAround(){
// 开启事务,spring给你开启
	try{
		buy(1001,10);
		System.out.println(spring的事务管理器.commit());
	}catch(Exception e){
		System.out.println(spring的事务管理器.rollback());
	}
}

2、大型项目使用AspectJ框架

在Spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。

  • 要使用的是aspectj框架,需要加入依赖
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>
  • 声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
  • 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)。
  • 配置AOP:指定哪些哪类要创建代理。

4、Spring项目实现事务示例

使用Spring框架进行事务管理通常涉及更多的配置步骤。下面是一个使用 @Transactional 注解的简单示例,演示了如何在 Spring 应用程序中进行事务管理。

首先,假设我们有一个 User 实体类和一个 UserRepository 接口:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    // getters and setters
}

public interface UserRepository extends JpaRepository<User, Long> {
    // Custom queries can be defined here
}

然后,我们有一个 UserService 类,其中包含一个使用 @Transactional 注解的方法:

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

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public User createUser(String name, String email) {
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        
        // 假设这里有两个数据库操作,需要确保它们要么都成功,要么都失败
        userRepository.save(user); // 第一次数据库操作
        
        // 模拟一个可能抛出异常的操作
        if ("invalid-email".equals(email)) {
            throw new RuntimeException("Invalid email address");
        }
        
        // 第二次数据库操作(这里仅作为示例,实际上可能没有第二次操作)
        // userRepository.updateSomething(user);
        
        return user;
    }
}

在上面的代码中,createUser 方法被标记为 @Transactional,这意味着该方法内的所有数据库操作都将在一个事务中执行。如果 email"invalid-email",则会抛出一个异常,导致事务回滚,user 不会被保存到数据库中。否则,user 将被成功保存。

注意,默认情况下,@Transactional 注解将事务传播行为设置为 Propagation.REQUIRED,这意味着如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。你还可以根据需要调整事务的传播行为、隔离级别、超时时间等属性。

在实际应用中,@Transactional 注解可以应用于类级别或方法级别,根据具体需求进行配置。同时,还需要确保 Spring 的事务管理器(如 DataSourceTransactionManager)已经配置好,并且相关的数据库连接和 JPA 配置也已经正确设置。

5、SpringBoot项目实现事务示例

以下是一个简单的Spring Boot项目示例,它演示了如何使用@Transactional注解来管理事务。

首先,我们需要一个实体类来表示数据库中的表

import javax.persistence.Entity;  
import javax.persistence.Id;  
  
@Entity  
public class User {  
    @Id  
    private Long id;  
    private String name;  
    private String email;  
  
    // 构造器、getter和setter方法省略  
}

接下来,我们需要一个JPA仓库接口来操作数据库

import org.springframework.data.jpa.repository.JpaRepository;  
  
public interface UserRepository extends JpaRepository<User, Long> {  
    User findByName(String name);  
}

然后,我们创建一个服务类,该类将使用@Transactional注解来确保事务的完整性

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class UserService {  
  
    @Autowired  
    private UserRepository userRepository;  
  
    @Transactional  
    public User createUser(User user) {  
        User savedUser = userRepository.save(user);  
        // 假设这里还有其他数据库操作,比如记录日志等  
        return savedUser;  
    }  
  
    @Transactional  
    public void updateUserEmail(Long userId, String newEmail) {  
        User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));  
        user.setEmail(newEmail);  
        userRepository.save(user);  
        // 假设这里还有其他数据库操作,比如更新审计记录等  
    }  
}

在上面的 UserService 类中,createUserupdateUserEmail 方法都被标记为 @Transactional ,这意味着当这些方法被调用时,Spring会创建一个新的事务(如果当前没有事务的话),并在方法执行完毕后提交事务。如果在方法执行期间抛出了任何运行时异常,事务将会被回滚,从而保证了数据的完整性和一致性。

最后,我们需要配置Spring Boot以启用JPA和事务管理

import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.boot.autoconfigure.domain.EntityScan;  
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;  
import org.springframework.context.annotation.EnableTransactionManagement;  
import org.springframework.transaction.annotation.EnableTransactionManagement;  
  
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})  
@EntityScan("com.example.demo.model") // 替换为你的实体类所在的包路径  
@EnableTransactionManagement  
public class DemoApplication {  
  
    public static void main(String[] args) {  
        SpringApplication.run(DemoApplication.class, args);  
    }  
}

在这个 DemoApplication 类中,我们使用了 @SpringBootApplication 注解来启用Spring Boot的自动配置功能,并通过 exclude 属性排除了 DataSourceAutoConfiguration ,因为我们可能想要自定义数据源配置。@EntityScan 注解指定了包含实体类的包路径。最后,@EnableTransactionManagement 注解启用了Spring的事务管理功能。

为了运行这个示例,还需要在 application.propertiesapplication.yml 文件中配置数据库连接信息,并添加相应的JPA和数据库驱动依赖到你的 pom.xml 文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值