Spring Batch 中 `ResourcelessTransactionManager` 和 `DataSourceTransactionManager` 的区别【完整项目详解对比】
·
创建一个全新的独立项目示例,详细讲解 Spring Batch 中 ResourcelessTransactionManager 和 DataSourceTransactionManager 的区别。

一、核心概念详解
1. DataSourceTransactionManager
Spring 框架提供的标准事务管理器,用于管理基于 JDBC 的数据库事务,确保数据的 ACID 特性。
2. ResourcelessTransactionManager
Spring Batch 提供的轻量级事务管理器,不管理任何实际资源,仅模拟事务行为,适用于无需持久化的场景。
二、完整项目示例
项目结构
spring-batch-transaction-demo/
├── pom.xml
├── src/main/java/com/example/batch/
│ ├── SpringBatchDemoApplication.java
│ ├── entity/
│ │ └── Order.java
│ ├── repository/
│ │ └── OrderRepository.java
│ ├── dto/
│ │ └── OrderDTO.java
│ ├── config/
│ │ ├── DataSourceTransactionConfig.java
│ │ ├── ResourcelessTransactionConfig.java
│ │ └── MixedTransactionConfig.java
│ ├── controller/
│ │ └── BatchController.java
│ └── service/
│ └── NotificationService.java
└── src/main/resources/
└── application.yml
1. Maven 依赖配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-batch-transaction-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Batch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 实体类
package com.example.batch.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number", nullable = false, unique = true)
private String orderNumber;
@Column(name = "customer_name", nullable = false)
private String customerName;
@Column(name = "amount", precision = 10, scale = 2)
private BigDecimal amount;
@Column(name = "status")
private String status;
@Column(name = "created_time")
private LocalDateTime createdTime;
}
3. DTO 类
package com.example.batch.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.math.BigDecimal;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderDTO {
private String orderNumber;
private String customerName;
private BigDecimal amount;
private String status;
}
4. Repository 接口
package com.example.batch.repository;
import com.example.batch.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
Order findByOrderNumber(String orderNumber);
}
5. 通知服务(模拟外部 API 调用)
package com.example.batch.service;
import com.example.batch.dto.OrderDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class NotificationService {
public void sendEmailNotification(OrderDTO order) {
log.info("=== 发送邮件通知 ===");
log.info("收件人: {}", order.getCustomerName());
log.info("订单号: {}", order.getOrderNumber());
log.info("金额: {}", order.getAmount());
log.info("邮件发送成功!");
}
public void sendSmsNotification(OrderDTO order) {
log.info("=== 发送短信通知 ===");
log.info("手机号: 138****1234");
log.info("订单号: {}", order.getOrderNumber());
log.info("短信发送成功!");
}
}
6. DataSourceTransactionManager 配置
package com.example.batch.config;
import com.example.batch.dto.OrderDTO;
import com.example.batch.entity.Order;
import com.example.batch.repository.OrderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@Configuration
@Slf4j
public class DataSourceTransactionConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private OrderRepository orderRepository;
@Autowired
private DataSource dataSource;
/**
* 创建 DataSourceTransactionManager
* 用于管理数据库事务
*/
@Bean(name = "dataSourceTransactionManager")
public PlatformTransactionManager dataSourceTransactionManager() {
return new org.springframework.jdbc.datasource.DataSourceTransactionManager(dataSource);
}
/**
* 读取订单项
*/
@Bean
public ItemReader<OrderDTO> dataSourceReader() {
List<OrderDTO> orders = Arrays.asList(
new OrderDTO("ORD001", "张三", new BigDecimal("100.00"), "COMPLETED"),
new OrderDTO("ORD002", "李四", new BigDecimal("200.50"), "COMPLETED"),
new OrderDTO("ORD003", "王五", new BigDecimal("350.75"), "FAILED"),
new OrderDTO("ORD004", "赵六", new BigDecimal("500.00"), "COMPLETED"),
new OrderDTO("ORD005", "孙七", new BigDecimal("125.30"), "COMPLETED"),
new OrderDTO("ORD006", "周八", new BigDecimal("890.00"), "COMPLETED")
);
return new ListItemReader<>(orders);
}
/**
* 处理订单数据
* 过滤掉失败的订单
*/
@Bean
public ItemProcessor<OrderDTO, Order> dataSourceProcessor() {
return orderDTO -> {
log.info("处理订单: {}", orderDTO.getOrderNumber());
if ("FAILED".equals(orderDTO.getStatus())) {
log.warn("订单 {} 状态为 FAILED,跳过处理", orderDTO.getOrderNumber());
return null;
}
Order order = new Order();
order.setOrderNumber(orderDTO.getOrderNumber());
order.setCustomerName(orderDTO.getCustomerName());
order.setAmount(orderDTO.getAmount());
order.setStatus("SAVED");
order.setCreatedTime(LocalDateTime.now());
return order;
};
}
/**
* 写入数据库
* 使用 DataSourceTransactionManager 管理事务
*/
@Bean
public ItemWriter<Order> dataSourceWriter() {
return items -> {
log.info("准备保存 {} 个订单到数据库", items.size());
for (Order order : items) {
orderRepository.save(order);
log.info("已保存订单: {} - {}", order.getOrderNumber(), order.getCustomerName());
}
log.info("所有订单已成功保存到数据库");
};
}
/**
* 配置 Step - 使用 DataSourceTransactionManager
* chunk size 为 2,每处理 2 条记录提交一次事务
*/
@Bean
public Step dataSourceStep() {
return stepBuilderFactory.get("dataSourceStep")
.<OrderDTO, Order>chunk(2)
.reader(dataSourceReader())
.processor(dataSourceProcessor())
.writer(dataSourceWriter())
.transactionManager(dataSourceTransactionManager())
.build();
}
/**
* 配置 Job
*/
@Bean
public Job dataSourceJob() {
return jobBuilderFactory.get("dataSourceJob")
.start(dataSourceStep())
.build();
}
}
7. ResourcelessTransactionManager 配置
package com.example.batch.config;
import com.example.batch.dto.OrderDTO;
import com.example.batch.service.NotificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
@Configuration
@Slf4j
public class ResourcelessTransactionConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private NotificationService notificationService;
/**
* 创建 ResourcelessTransactionManager
* 不管理任何实际资源,仅模拟事务行为
*/
@Bean(name = "resourcelessTransactionManager")
public PlatformTransactionManager resourcelessTransactionManager() {
return new ResourcelessTransactionManager();
}
/**
* 读取待通知的订单
*/
@Bean
public ItemReader<OrderDTO> resourcelessReader() {
List<OrderDTO> orders = Arrays.asList(
new OrderDTO("NOTIFY001", "客户A", new BigDecimal("100.00"), "COMPLETED"),
new OrderDTO("NOTIFY002", "客户B", new BigDecimal("200.50"), "COMPLETED"),
new OrderDTO("NOTIFY003", "客户C", new BigDecimal("350.75"), "COMPLETED"),
new OrderDTO("NOTIFY004", "客户D", new BigDecimal("500.00"), "COMPLETED"),
new OrderDTO("NOTIFY005", "客户E", new BigDecimal("125.30"), "COMPLETED")
);
return new ListItemReader<>(orders);
}
/**
* 处理订单通知数据
*/
@Bean
public ItemProcessor<OrderDTO, OrderDTO> resourcelessProcessor() {
return orderDTO -> {
log.info("准备发送通知: {}", orderDTO.getOrderNumber());
if (orderDTO.getAmount().compareTo(new BigDecimal("1000")) > 0) {
log.info("订单金额超过 1000,标记为重要通知");
orderDTO.setStatus("PRIORITY");
}
return orderDTO;
};
}
/**
* 发送通知(邮件 + 短信)
* 使用 ResourcelessTransactionManager,因为不涉及数据库操作
*/
@Bean
public ItemWriter<OrderDTO> resourcelessWriter() {
return items -> {
log.info("=== 开始批量发送通知,共 {} 个订单 ===", items.size());
for (OrderDTO order : items) {
notificationService.sendEmailNotification(order);
notificationService.sendSmsNotification(order);
log.info("订单 {} 的通知已发送", order.getOrderNumber());
}
log.info("=== 所有通知发送完成 ===");
};
}
/**
* 配置 Step - 使用 ResourcelessTransactionManager
* chunk size 为 3,每处理 3 条记录"提交"一次(实际无操作)
*/
@Bean
public Step resourcelessStep() {
return stepBuilderFactory.get("resourcelessStep")
.<OrderDTO, OrderDTO>chunk(3)
.reader(resourcelessReader())
.processor(resourcelessProcessor())
.writer(resourcelessWriter())
.transactionManager(resourcelessTransactionManager())
.build();
}
/**
* 配置 Job
*/
@Bean
public Job resourcelessJob() {
return jobBuilderFactory.get("resourcelessJob")
.start(resourcelessStep())
.build();
}
}
8. 混合事务配置(同时使用两种事务管理器)
package com.example.batch.config;
import com.example.batch.dto.OrderDTO;
import com.example.batch.entity.Order;
import com.example.batch.repository.OrderRepository;
import com.example.batch.service.NotificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@Configuration
@Slf4j
public class MixedTransactionConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private OrderRepository orderRepository;
@Autowired
private DataSource dataSource;
@Autowired
private NotificationService notificationService;
@Bean(name = "mixedDataSourceTransactionManager")
public PlatformTransactionManager mixedDataSourceTransactionManager() {
return new org.springframework.jdbc.datasource.DataSourceTransactionManager(dataSource);
}
@Bean(name = "mixedResourcelessTransactionManager")
public PlatformTransactionManager mixedResourcelessTransactionManager() {
return new ResourcelessTransactionManager();
}
@Bean
public ItemReader<OrderDTO> mixedReader() {
List<OrderDTO> orders = Arrays.asList(
new OrderDTO("MIX001", "混合用户1", new BigDecimal("100.00"), "NEW"),
new OrderDTO("MIX002", "混合用户2", new BigDecimal("200.50"), "NEW"),
new OrderDTO("MIX003", "混合用户3", new BigDecimal("350.75"), "NEW")
);
return new ListItemReader<>(orders);
}
@Bean
public ItemProcessor<OrderDTO, OrderDTO> mixedProcessor() {
return orderDTO -> {
log.info("验证订单: {}", orderDTO.getOrderNumber());
orderDTO.setStatus("VALIDATED");
return orderDTO;
};
}
/**
* Step 1: 保存订单到数据库
* 使用 DataSourceTransactionManager
*/
@Bean
public ItemWriter<OrderDTO> databaseWriter() {
return items -> {
log.info(">>> 数据库写入阶段:保存 {} 个订单", items.size());
for (OrderDTO dto : items) {
Order order = new Order();
order.setOrderNumber(dto.getOrderNumber());
order.setCustomerName(dto.getCustomerName());
order.setAmount(dto.getAmount());
order.setStatus("DB_SAVED");
order.setCreatedTime(LocalDateTime.now());
orderRepository.save(order);
log.info("订单已入库: {}", order.getOrderNumber());
}
};
}
/**
* Step 2: 发送通知
* 使用 ResourcelessTransactionManager
*/
@Bean
public ItemWriter<OrderDTO> notificationWriter() {
return items -> {
log.info(">>> 通知发送阶段:处理 {} 个订单", items.size());
for (OrderDTO order : items) {
notificationService.sendEmailNotification(order);
log.info("通知已发送: {}", order.getOrderNumber());
}
};
}
@Bean
public Step saveToDatabaseStep() {
return stepBuilderFactory.get("saveToDatabaseStep")
.<OrderDTO, OrderDTO>chunk(2)
.reader(mixedReader())
.processor(mixedProcessor())
.writer(databaseWriter())
.transactionManager(mixedDataSourceTransactionManager())
.build();
}
@Bean
public Step sendNotificationStep() {
return stepBuilderFactory.get("sendNotificationStep")
.<OrderDTO, OrderDTO>chunk(2)
.reader(mixedReader())
.processor(mixedProcessor())
.writer(notificationWriter())
.transactionManager(mixedResourcelessTransactionManager())
.build();
}
/**
* 混合事务 Job
* Step 1 使用 DataSourceTransactionManager 保存数据
* Step 2 使用 ResourcelessTransactionManager 发送通知
*/
@Bean
public Job mixedTransactionJob() {
return jobBuilderFactory.get("mixedTransactionJob")
.start(saveToDatabaseStep())
.next(sendNotificationStep())
.build();
}
}
9. Controller 控制器
package com.example.batch.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/batch")
@Slf4j
public class BatchController {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job dataSourceJob;
@Autowired
private Job resourcelessJob;
@Autowired
private Job mixedTransactionJob;
/**
* 运行使用 DataSourceTransactionManager 的任务
*/
@GetMapping("/run/datasource")
public String runDataSourceJob() {
try {
log.info("========== 启动 DataSourceTransactionManager 任务 ==========");
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
JobExecution execution = jobLauncher.run(dataSourceJob, jobParameters);
String result = String.format(
"DataSource Job 执行完成 - 状态: %s, 退出码: %s",
execution.getStatus(),
execution.getExitStatus()
);
log.info(result);
return result;
} catch (Exception e) {
log.error("DataSource Job 执行失败", e);
return "执行失败: " + e.getMessage();
}
}
/**
* 运行使用 ResourcelessTransactionManager 的任务
*/
@GetMapping("/run/resourceless")
public String runResourcelessJob() {
try {
log.info("========== 启动 ResourcelessTransactionManager 任务 ==========");
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
JobExecution execution = jobLauncher.run(resourcelessJob, jobParameters);
String result = String.format(
"Resourceless Job 执行完成 - 状态: %s, 退出码: %s",
execution.getStatus(),
execution.getExitStatus()
);
log.info(result);
return result;
} catch (Exception e) {
log.error("Resourceless Job 执行失败", e);
return "执行失败: " + e.getMessage();
}
}
/**
* 运行混合事务任务
*/
@GetMapping("/run/mixed")
public String runMixedJob() {
try {
log.info("========== 启动混合事务任务 ==========");
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
JobExecution execution = jobLauncher.run(mixedTransactionJob, jobParameters);
String result = String.format(
"Mixed Job 执行完成 - 状态: %s, 退出码: %s",
execution.getStatus(),
execution.getExitStatus()
);
log.info(result);
return result;
} catch (Exception e) {
log.error("Mixed Job 执行失败", e);
return "执行失败: " + e.getMessage();
}
}
/**
* 一次性运行所有任务进行对比
*/
@GetMapping("/run/all")
public String runAllJobs() {
StringBuilder result = new StringBuilder();
result.append(runDataSourceJob()).append("\n\n");
result.append("================================\n\n");
result.append(runResourcelessJob()).append("\n\n");
result.append("================================\n\n");
result.append(runMixedJob());
return result.toString();
}
}
10. 主应用类
package com.example.batch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchDemoApplication.class, args);
}
}
11. 应用配置文件 (application.yml)
server:
port: 8080
spring:
datasource:
url: jdbc:h2:mem:batchdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
h2:
console:
enabled: true
path: /h2-console
batch:
job:
enabled: false
initialize-schema: always
logging:
level:
root: INFO
com.example.batch: DEBUG
org.springframework.batch: INFO
org.springframework.transaction: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
三、运行测试
启动应用后访问:
-
测试 DataSourceTransactionManager:
GET http://localhost:8080/api/batch/run/datasource -
测试 ResourcelessTransactionManager:
GET http://localhost:8080/api/batch/run/resourceless -
测试混合事务:
GET http://localhost:8080/api/batch/run/mixed -
查看所有对比:
GET http://localhost:8080/api/batch/run/all -
查看 H2 数据库:
访问 http://localhost:8080/h2-console JDBC URL: jdbc:h2:mem:batchdb 用户名: sa 密码: (空)
四、全面对比总结表
| 对比维度 | DataSourceTransactionManager | ResourcelessTransactionManager |
|---|---|---|
| 包路径 | org.springframework.jdbc.datasource |
org.springframework.batch.support.transaction |
| 管理资源 | DataSource(数据库连接) | 无任何实际资源 |
| 事务提交 | 执行真实的 connection.commit() |
仅更新内部状态标志 |
| 事务回滚 | 执行真实的 connection.rollback() |
仅更新内部状态标志 |
| ACID 保证 | ✅ 完全支持 | ❌ 不提供 |
| 性能开销 | 高(连接获取/释放 + DB 交互) | 极低(纯内存操作) |
| 并发控制 | 通过数据库锁机制 | 无 |
| 隔离级别 | 支持 READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE | 不支持 |
| 传播行为 | 支持 REQUIRED、REQUIRES_NEW、NESTED 等全部 7 种 | 仅模拟,无实际效果 |
| 适用场景 | 数据库 CRUD 操作 | 文件 I/O、API 调用、邮件发送、日志记录 |
| 典型用例 | ItemWriter 写入数据库 | 调用 REST API、发送消息队列、生成报表文件 |
| 资源锁定 | 会锁定数据库行/表 | 无锁定 |
| 错误恢复 | 可回滚已修改的数据 | 无法回滚(无实际操作) |
| 幂等性要求 | 由事务保证 | 需业务层自行实现 |
| 重启恢复 | Spring Batch 可从断点恢复,数据一致 | 需自行处理重复执行问题 |
| 分布式事务 | 可作为 XA 资源参与 | 不参与 |
| 测试友好性 | 需要数据库环境或 Mock | 无需任何外部依赖 |
| 配置复杂度 | 需配置 DataSource | 无需配置,直接 new |
| 内存占用 | 较高(连接池、事务上下文) | 极低 |
| 日志追踪 | 可通过数据库事务日志审计 | 仅应用日志 |
| Chunk 提交 | 每个 chunk 结束提交数据库事务 | 每个 chunk 结束仅更新状态 |
| Step 类型 | Tasklet、Chunk(含 DB 操作) | Tasklet、Chunk(无 DB 操作) |
| 线程安全 | 是(基于 ThreadLocal) | 是(无状态) |
| 嵌套事务 | 支持 | 不支持(无实际意义) |
| 超时控制 | 支持事务超时 | 不支持 |
| 只读优化 | 支持 setReadOnly(true) 优化 | 无意义 |
| 生产环境 | ✅ 推荐用于关键业务 | ⚠️ 仅限非关键操作 |
| 开发效率 | 中等(需配置 DB) | 高(开箱即用) |
五、选择决策树
需要事务管理?
├─ 涉及数据库操作?
│ ├─ 是 → DataSourceTransactionManager ✅
│ │ ├─ 需要写入数据库
│ │ ├─ 需要保证数据一致性
│ │ └─ 多个 DB 操作需原子性
│ │
│ └─ 否 → 继续判断 ↓
│
└─ 仅外部系统调用?
├─ 调用 REST API → ResourcelessTransactionManager ✅
├─ 发送邮件/短信 → ResourcelessTransactionManager ✅
├─ 文件读写 → ResourcelessTransactionManager ✅
├─ 消息队列 → ResourcelessTransactionManager ✅
└─ 纯内存计算 → ResourcelessTransactionManager ✅
六、最佳实践建议
使用 DataSourceTransactionManager 的场景:
✅ 电商订单入库
✅ 用户注册信息保存
✅ 金融交易记录
✅ 库存扣减操作
✅ 任何需要持久化且保证一致性的数据
使用 ResourcelessTransactionManager 的场景:
✅ 批量发送营销邮件
✅ 调用第三方支付接口
✅ 生成并导出 Excel/PDF 文件
✅ 同步数据到搜索引擎(Elasticsearch)
✅ 单元测试和集成测试
✅ 开发阶段快速原型验证
混合使用策略:
在一个完整的业务流程中,可以结合两者优势:
- Step 1:使用
DataSourceTransactionManager保存订单到数据库 - Step 2:使用
ResourcelessTransactionManager调用支付接口 - Step 3:使用
ResourcelessTransactionManager发送确认邮件 - Step 4:使用
DataSourceTransactionManager更新订单状态
这样既保证了核心数据的 consistency,又避免了对外部操作的不必要事务开销!
更多推荐




所有评论(0)