Spring Boot教程之十六:使用 @Transactional 注释进行事务管理

Spring Boot – 使用 @Transactional 注释进行事务管理

@Transactional注释是用于管理 Spring Boot 应用程序中的事务的元数据。要配置Spring Transaction,可以在类级别或方法级别应用此注释。在企业应用程序中,事务是应用程序执行的一系列操作,这些操作一起流水线执行单个操作。例如,预订机票也是一项交易,最终用户必须输入其信息,然后付款才能预订机票。

为什么我们需要事务管理?

让我们通过上面的例子来理解交易,如果用户输入了他的信息,那么用户的信息就会存储在 user_info 表中。现在,为了订票,他进行了在线支付,但由于某种原因(系统故障),付款被取消了,所以他没有订票。但是,问题是他的信息存储在 user_info 表中。从大范围来看,一天内会发生数千件这样的事情。因此,存储交易的单个操作并不是一个好的做法(这里只存储用户信息,而不存储支付信息)。

为了克服这些问题,spring 提供了事务管理,它使用注释来处理这些问题。在这种情况下,spring 将用户信息存储在临时内存中,然后检查付款信息,如果付款成功,则它将完成事务,否则它将回滚事务,并且用户信息将不会存储在数据库中。

@Transactional 注解

在 Spring Boot 中,@Transactional 注释用于管理 Spring Boot 应用程序中的事务,并用于定义事务的范围。此注释可应用于类级别或方法级别。它提供数据可靠性和一致性。当使用 @Transactional 注释指示方法时,它表示应在该事务的上下文中执行特定方法。如果事务成功,则提交对数据库所做的更改,如果任何事务失败,则对该特定事务所做的所有更改都可以回滚,这将确保数据库保持一致状态。

注意:要使用@Transactional注释,您需要使用@EnableTransactionManagement在Spring Boot应用程序的主类中配置事务管理。

在 Spring Boot 中配置事务

在这个例子中,我们将创建一个应用程序来存储用户信息及其地址信息,并将使用 spring 事务管理来解决事务中断问题。

注释@Transactional允许您以声明方式定义事务边界。对于那些希望加深对 Spring Boot 功能的理解的人来说,Java Backend课程涵盖了保护应用程序的基本安全实践。

事务管理的逐步实施

步骤1:创建Spring Boot项目

在此步骤中,我们将创建一个 Spring Boot 项目。为此,我们将使用Spring Initializr

第 2 步:添加依赖项

我们将为我们的 Spring Boot 应用程序添加所需的依赖项。

步骤3:配置数据库

现在,我们将在应用程序中配置数据库。我们将使用以下配置并将其添加到我们的application.properties文件中。

server.port = 9090
#database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/employee_db
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
#the ddl-auto=update : It will create the entity schema and map it to db automatically
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

注意:请添加您的数据库用户名和密码以及数据库路径。

步骤4:创建模型类

在此步骤中,我们将创建模型类。在这里,我们将创建两个模型类,EmployeeAddress。在创建模型类时,我们将使用Lombok 库

Employee.java

Java

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
@Table(name="EMP_INFO")
public class Employee {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    
}

Address.java

Java

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
@Table(name="ADD_INFO")
public class Address {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String address;
  
    // one to one mapping means,
    // one employee stays at one address only
    @OneToOne
    private Employee employee;

}

步骤 5:创建数据库层

在此步骤中,我们将创建一个数据库层。为此,我们将创建EmployeeRepositoryAddressRepository,并将扩展JpaRepository<T, ID>以执行与数据库相关的查询。

EmployeeRepository.java

Java

import org.springframework.data.jpa.repository.JpaRepository;
import com.geeksforgeeks.transactionmanagement.model.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

}

AddressRepository.java

Java

import org.springframework.data.jpa.repository.JpaRepository;
import com.geeksforgeeks.transactionmanagement.model.Address;

public interface AddressRepository extends JpaRepository<Address, Integer> {

}

步骤 6:创建服务层

您可以在服务层中使用@Transactional注释,这将导致与数据库交互。在此步骤中,我们将为我们的应用程序创建一个服务层并向其添加业务逻辑。为此,我们将创建两个类EmployeeServiceAddressService。在 EmployeeService 类中,我们抛出了一个异常。

EmployeeService.java

Java

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

import com.geeksforgeeks.transactionmanagement.model.Address;
import com.geeksforgeeks.transactionmanagement.model.Employee;
import com.geeksforgeeks.transactionmanagement.repository.EmployeeRepository;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Autowired
    private AddressService addressService;
    
    @Transactional
    public Employee addEmployee(Employee employee) throws Exception {
        Employee employeeSavedToDB = this.employeeRepository.save(employee);
        
        Address address = new Address();
        address.setId(123L);
        address.setAddress("Varanasi");
        address.setEmployee(employee);
        
        // calling addAddress() method 
        // of AddressService class
        this.addressService.addAddress(address);
        return employeeSavedToDB;
    }
}

AddressService.java

Java

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

import com.geeksforgeeks.transactionmanagement.model.Address;
import com.geeksforgeeks.transactionmanagement.repository.AddressRepository;

@Service
public class AddressService {
    
    @Autowired
    private AddressRepository addressRepository;
    
    public Address addAddress(Address address) {
        Address addressSavedToDB = this.addressRepository.save(address);
        return addressSavedToDB;
    }

}

步骤 7:创建控制器

在此步骤中,我们将为我们的应用程序创建一个控制器。为此,我们将创建一个 Controller 类并向其添加所有映射。

Controller.java

Java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.geeksforgeeks.transactionmanagement.model.Employee;
import com.geeksforgeeks.transactionmanagement.service.EmployeeService;

@RestController
@RequestMapping("/api/employee")
public class Controller {
    
    @Autowired
    private EmployeeService employeeService;
    
    @PostMapping("/add")
    public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee) throws Exception{
        Employee employeeSavedToDB = this.employeeService.addEmployee(employee);
        return new ResponseEntity<Employee>(employeeSavedToDB, HttpStatus.CREATED);
    }
}

步骤 8:运行我们的应用程序

在此步骤中,我们将运行我们的应用程序。一旦我们使用 Hibernate 映射运行我们的应用程序,我们将在数据库中创建所需的表。

从日志中我们可以看到,我们的表已经创建。我们也可以通过查看数据库来确认这一点。

现在,我们将使用 Postman 请求我们的应用程序添加一名员工。一旦我们收到请求,请求就会从控制器移动到我们的业务逻辑所在的服务层

从上面的响应中我们可以看到,我们添加了一名员工。我们还可以检查数据库中的员工数据和地址数据。

类似地,我们也可以检查地址数据。

步骤 9:没有事务管理的问题

在 EmployeeService 类中,我们将地址对象初始化为 NULL。因此,由于地址对象为空,员工的详细信息无法存储在数据库中。但是,由于我们没有使用事务管理,员工基本信息将保留在数据库中。由于地址对象为空,因此省略了地址详细信息。

注意:如果没有使用@EnableTransactionManagement ,那么将@Transactional注释应用于方法将不会触发任何操作的回滚。

Java

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

import com.geeksforgeeks.transactionmanagement.model.Address;
import com.geeksforgeeks.transactionmanagement.model.Employee;
import com.geeksforgeeks.transactionmanagement.repository.EmployeeRepository;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Autowired
    private AddressService addressService;
    
    public Employee addEmployee(Employee employee) throws Exception {
        Employee employeeSavedToDB = this.employeeRepository.save(employee);
        
        // we will initialize the
        // address object as null
        Address address = null;
        address.setId(123L);
        address.setAddress("Varanasi");
        address.setEmployee(employee);
        
        // calling addAddress() method
        // of AddressService class
        this.addressService.addAddress(address);
        return employeeSavedToDB;
    }
}

现在,我们将从数据库中删除我们的表并再次运行我们的应用程序,并请求应用程序创建一个员工。

我们将地址对象初始化为空并请求应用程序,我们在数据库中创建了一名员工,但地址信息却没有,因为我们收到了空指针异常。但是,这在事务管理中并不是一个好的做法,因为只有在保存地址时才应该保存员工,反之亦然。

步骤 10:事务管理

为了解决这个问题,我们将使用@Transactional注释。这将确保交易完成。也就是说,要么存储员工和地址数据,要么不存储任何数据。要使用事务管理,我们需要在 Spring Boot 应用程序的主类中使用 @EnableTransactionManagement,并且还需要使用@Transactional注释注释EmployeeService 类中的addEmployee()方法。

TransactionManagementApplication.java

Java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
public class TransactionManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(TransactionManagementApplication.class, args);
    }

}

EmployeeService.java

Java

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

import com.geeksforgeeks.transactionmanagement.model.Address;
import com.geeksforgeeks.transactionmanagement.model.Employee;
import com.geeksforgeeks.transactionmanagement.repository.EmployeeRepository;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Autowired
    private AddressService addressService;
    
    @Transactional
    public Employee addEmployee(Employee employee) throws Exception {
        Employee employeeSavedToDB = this.employeeRepository.save(employee);
        
        // we will initialize the 
        // address object as null
        Address address = null;
        address.setId(123L);
        address.setAddress("Varanasi");
        address.setEmployee(employee);
        
        // calling addAddress() method
        // of AddressService class
        this.addressService.addAddress(address);
        return employeeSavedToDB;
    }
}

步骤11:运行应用程序

现在,我们已为应用程序启用了事务管理。我们将再次从数据库中删除表并请求应用程序添加员工。

这次员工数据没有存储在数据库中,地址数据也没有。这样,spring 就处理了员工和地址数据都存储或不存储任何数据的事务。

结论

在本文中,我们学习了在 Spring Boot 应用程序中使用事务管理的基本配置。我们还介绍了 @Transactional 和 @EnableTransactionManagement 注释及其在 Spring Boot 应用程序中的逐步实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潜洋

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值