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:创建模型类
在此步骤中,我们将创建模型类。在这里,我们将创建两个模型类,Employee和Address。在创建模型类时,我们将使用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:创建数据库层
在此步骤中,我们将创建一个数据库层。为此,我们将创建EmployeeRepository和AddressRepository,并将扩展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注释,这将导致与数据库交互。在此步骤中,我们将为我们的应用程序创建一个服务层并向其添加业务逻辑。为此,我们将创建两个类EmployeeService和AddressService。在 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 应用程序中的逐步实现。