Spring Batch 3.0.2升级后非JPA对象持久化失败求助
Spring Batch 3.0 版本后,Spring Boot 自动配置会优先使用 JpaJobRepository(当引入spring-boot-starter-data-jpa依赖时),此时步骤绑定的JpaTransactionManager会将 chunk 处理过程纳入 JPA 事务上下文。Hibernate 在事务同步过程中会检查上下文中的对象是否为 JPA 实体,而org.apache.poi.ss.usermodel.Row既不是 JPA 实体也未实现Serializable,因此触发UnknownEntityTypeException。
而 Spring Batch 2.6.x 中默认使用基于 JDBC 的 JobRepository,事务管理不依赖 JPA 上下文,所以不会出现该问题。
以下是两种生产环境可行的解决方式:
1. 将 POI Row 转换为自定义 DTO(推荐)
直接传递 POI 的Row对象作为 Batch Item 并非最佳实践,因为它是 POI 内部对象,不适合在 Batch 流程中传递和持久化检查。建议在 Reader 中提前将Row的业务数据提取到自定义 DTO 中:
// 自定义DTO,实现Serializable(可选,但Batch推荐) public class ExcelRowDTO implements Serializable { private String cellA; private Integer cellB; // 构造器、getter/setter } // 修改自定义Reader,输出DTO而非Row @Bean public ItemReader<ExcelRowDTO> excelItemReader() { return new ItemReader<>() { @Override public ExcelRowDTO read() throws Exception { Row row = // 从POI中读取Row if (row == null) { return null; } // 转换为DTO ExcelRowDTO dto = new ExcelRowDTO(); dto.setCellA(row.getCell(0).getStringCellValue()); dto.setCellB((int) row.getCell(1).getNumericCellValue()); return dto; } }; } // 修改Step的泛型 @Bean @JobScope public Step processFileStep() { return new StepBuilder("process-file", jobRepository) .<ExcelRowDTO, Matching>chunk(chunkSize, transactionManager) .reader(excelItemReader()) .processor(processor()) .writer(writer()) .build(); }
这种方式从根源上避免了非实体对象进入 JPA 事务上下文,同时让 Batch Item 更贴合业务逻辑。
2. 为 Batch 配置独立的 JDBC 事务管理器
如果必须保留Row作为 Item,可以为 Spring Batch 单独配置基于DataSourceTransactionManager的事务管理,与 JPA 事务上下文隔离:
// 配置Batch专用的事务管理器 @Bean public DataSourceTransactionManager batchTransactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } // 配置基于JDBC的JobRepository @Bean public JobRepository batchJobRepository(DataSource dataSource, DataSourceTransactionManager batchTransactionManager) throws Exception { JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTransactionManager(batchTransactionManager); factoryBean.setTablePrefix("batch.batch_"); // 与原配置保持一致 factoryBean.afterPropertiesSet(); return factoryBean.getObject(); } // 修改Step,使用Batch专用的事务管理器和JobRepository @Bean @JobScope public Step processFileStep(JobRepository batchJobRepository, DataSourceTransactionManager batchTransactionManager) { return new StepBuilder("process-file", batchJobRepository) .<Row, Matching>chunk(chunkSize, batchTransactionManager) .reader(reader()) .processor(processor()) .writer(writer()) .build(); }
注意:需要确保原@EnableBatchProcessing配置不会覆盖自定义的 JobRepository,或者可以移除@EnableBatchProcessing,完全手动配置 Batch 核心组件。
内容的提问来源于stack exchange,提问作者ahgpoug

