微信登录

事务管理 - 声明式事务 - 注解或 XML 配置事务

Java - Web - Spring 《事务管理 - 声明式事务 - 注解或 XML 配置事务》

在 Java Web 开发中,Spring 框架提供了强大的事务管理功能,能够帮助开发者轻松处理数据库事务。声明式事务是 Spring 事务管理的一种重要方式,它允许我们通过注解或 XML 配置来定义事务,而不需要在业务代码中手动编写事务管理的代码,从而提高了代码的可维护性和可复用性。本文将详细介绍如何使用注解和 XML 配置来实现声明式事务,并给出相应的演示代码。

1. 声明式事务概述

声明式事务是基于 AOP(面向切面编程)实现的,它将事务管理的逻辑从业务逻辑中分离出来。在 Spring 中,声明式事务可以通过两种方式进行配置:注解和 XML。

1.1 注解配置事务

注解配置事务是一种更为简洁和直观的方式,通过在方法或类上添加 @Transactional 注解来声明事务。

1.2 XML 配置事务

XML 配置事务则是通过在 Spring 的配置文件中定义事务管理器和事务切面,然后将它们应用到需要事务管理的方法上。

2. 环境准备

在开始之前,我们需要确保项目中已经引入了 Spring 框架和相关的依赖。这里以 Maven 为例,在 pom.xml 中添加以下依赖:

  1. <dependencies>
  2. <!-- Spring 核心 -->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-context</artifactId>
  6. <version>5.3.23</version>
  7. </dependency>
  8. <!-- Spring JDBC -->
  9. <dependency>
  10. <groupId>org.springframework</groupId>
  11. <artifactId>spring-jdbc</artifactId>
  12. <version>5.3.23</version>
  13. </dependency>
  14. <!-- MySQL 驱动 -->
  15. <dependency>
  16. <groupId>mysql</groupId>
  17. <artifactId>mysql-connector-java</artifactId>
  18. <version>8.0.26</version>
  19. </dependency>
  20. </dependencies>

3. 注解配置事务示例

3.1 数据库表结构

假设我们有一个简单的 users 表,用于存储用户信息:

  1. CREATE TABLE users (
  2. id INT PRIMARY KEY AUTO_INCREMENT,
  3. name VARCHAR(50) NOT NULL,
  4. balance DECIMAL(10, 2) NOT NULL
  5. );

3.2 实体类

创建一个 User 实体类来映射数据库表:

  1. public class User {
  2. private Integer id;
  3. private String name;
  4. private Double balance;
  5. // 构造方法、Getter 和 Setter 省略
  6. }

3.3 DAO 层

创建一个 UserDao 接口和实现类,用于操作 users 表:

  1. import org.springframework.jdbc.core.JdbcTemplate;
  2. import org.springframework.stereotype.Repository;
  3. @Repository
  4. public class UserDao {
  5. private JdbcTemplate jdbcTemplate;
  6. public UserDao(JdbcTemplate jdbcTemplate) {
  7. this.jdbcTemplate = jdbcTemplate;
  8. }
  9. public void updateBalance(int userId, double amount) {
  10. String sql = "UPDATE users SET balance = balance +? WHERE id =?";
  11. jdbcTemplate.update(sql, amount, userId);
  12. }
  13. }

3.4 服务层

创建一个 UserService 类,使用 @Transactional 注解来声明事务:

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.stereotype.Service;
  3. import org.springframework.transaction.annotation.Transactional;
  4. @Service
  5. public class UserService {
  6. @Autowired
  7. private UserDao userDao;
  8. @Transactional
  9. public void transferMoney(int fromUserId, int toUserId, double amount) {
  10. // 扣除转出用户的余额
  11. userDao.updateBalance(fromUserId, -amount);
  12. // 模拟异常
  13. if (amount > 100) {
  14. throw new RuntimeException("转账金额不能超过 100");
  15. }
  16. // 增加转入用户的余额
  17. userDao.updateBalance(toUserId, amount);
  18. }
  19. }

3.5 配置类

创建一个 Spring 配置类,启用注解事务管理:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.jdbc.datasource.DriverManagerDataSource;
  5. import org.springframework.jdbc.core.JdbcTemplate;
  6. import org.springframework.transaction.annotation.EnableTransactionManagement;
  7. import javax.sql.DataSource;
  8. @Configuration
  9. @ComponentScan(basePackages = "com.example")
  10. @EnableTransactionManagement
  11. public class AppConfig {
  12. @Bean
  13. public DataSource dataSource() {
  14. DriverManagerDataSource dataSource = new DriverManagerDataSource();
  15. dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
  16. dataSource.setUrl("jdbc:mysql://localhost:3306/test");
  17. dataSource.setUsername("root");
  18. dataSource.setPassword("password");
  19. return dataSource;
  20. }
  21. @Bean
  22. public JdbcTemplate jdbcTemplate(DataSource dataSource) {
  23. return new JdbcTemplate(dataSource);
  24. }
  25. }

3.6 测试代码

编写一个测试类来验证事务是否正常工作:

  1. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  2. public class Main {
  3. public static void main(String[] args) {
  4. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  5. UserService userService = context.getBean(UserService.class);
  6. try {
  7. userService.transferMoney(1, 2, 50);
  8. System.out.println("转账成功");
  9. } catch (Exception e) {
  10. System.out.println("转账失败:" + e.getMessage());
  11. }
  12. context.close();
  13. }
  14. }

4. XML 配置事务示例

4.1 修改配置文件

创建一个 applicationContext.xml 配置文件,配置数据源、事务管理器和事务切面:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:tx="http://www.springframework.org/schema/tx"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/tx
  9. http://www.springframework.org/schema/tx/spring-tx.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12. <!-- 数据源 -->
  13. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  14. <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
  15. <property name="url" value="jdbc:mysql://localhost:3306/test"/>
  16. <property name="username" value="root"/>
  17. <property name="password" value="password"/>
  18. </bean>
  19. <!-- JdbcTemplate -->
  20. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  21. <property name="dataSource" ref="dataSource"/>
  22. </bean>
  23. <!-- 事务管理器 -->
  24. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  25. <property name="dataSource" ref="dataSource"/>
  26. </bean>
  27. <!-- 启用事务注解 -->
  28. <tx:annotation-driven transaction-manager="transactionManager"/>
  29. <!-- 扫描组件 -->
  30. <context:component-scan base-package="com.example"/>
  31. </beans>

4.2 修改测试代码

修改测试代码,使用 XML 配置文件来加载 Spring 上下文:

  1. import org.springframework.context.ApplicationContext;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. public class Main {
  4. public static void main(String[] args) {
  5. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  6. UserService userService = context.getBean(UserService.class);
  7. try {
  8. userService.transferMoney(1, 2, 50);
  9. System.out.println("转账成功");
  10. } catch (Exception e) {
  11. System.out.println("转账失败:" + e.getMessage());
  12. }
  13. ((ClassPathXmlApplicationContext) context).close();
  14. }
  15. }

5. 注解和 XML 配置事务的对比

配置方式 优点 缺点 适用场景
注解配置 简洁直观,代码侵入性小,易于维护 配置信息分散在代码中,不利于统一管理 小型项目或对代码简洁性要求较高的项目
XML 配置 配置信息集中,便于统一管理和维护 配置文件较为繁琐,代码侵入性大 大型项目或需要统一管理事务配置的项目

6. 总结

声明式事务是 Spring 框架提供的一种强大的事务管理方式,通过注解或 XML 配置可以轻松实现事务的管理。注解配置适合小型项目,而 XML 配置则更适合大型项目。在实际开发中,我们可以根据项目的具体需求选择合适的配置方式。通过本文的示例代码,相信你已经掌握了如何使用注解和 XML 配置来实现声明式事务。