Appearance
Spring Batch 平面文件处理指南
1. 平面文件概述
1.1 什么是平面文件?
平面文件(Flat File)是一种常见的数据交换格式,广泛应用于批量数据处理场景。与XML不同,平面文件没有统一的标准结构,因此读取前必须明确了解其格式规范。
NOTE
平面文件是许多遗留系统和金融系统常用的数据交换格式,掌握其处理技巧对批处理开发至关重要
1.2 平面文件类型对比
类型 | 特点 | 适用场景 | 示例 |
---|---|---|---|
分隔符文件 | 字段间用特定字符分隔 | CSV、TSV等通用数据格式 | John,Doe,30 |
固定长度文件 | 每个字段占用固定字节长度 | 银行交易记录、主机系统数据 | John Doe 30 |
选择建议
- ✅ 分隔符文件:字段长度不固定,数据包含文本内容时
- ✅ 固定长度文件:处理遗留系统数据或需要严格对齐的场景
2. 核心组件解析
2.1 FieldSet - 数据映射桥梁
FieldSet
是Spring Batch用于封装原始文件行数据的核心组件,提供类型安全的数据访问方法:
kotlin
val fieldSet: FieldSet = // 从文件行解析得到
// 按索引获取数据
val firstName = fieldSet.readString(0) // 第1列
val lastName = fieldSet.readString(1) // 第2列
val age = fieldSet.readInt(2) // 第3列
// 按列名获取数据(需配置列名映射)
val email = fieldSet.readString("email")
2.2 FlatFileItemReader - 文件读取器
基础配置示例
kotlin
@Bean
fun customerReader(): FlatFileItemReader<Customer> {
return FlatFileItemReaderBuilder<Customer>()
.name("customerReader")
.resource(FileSystemResource("data/customers.csv"))
.delimited() // [!code highlight] // 声明为分隔符文件
.delimiter(",") // [!code highlight] // 指定逗号为分隔符
.names("firstName", "lastName", "email") // [!code highlight] // 列名映射
.fieldSetMapper(object : FieldSetMapper<Customer> {
override fun mapFieldSet(fieldSet: FieldSet): Customer {
return Customer(
firstName = fieldSet.readString("firstName"),
lastName = fieldSet.readString("lastName"),
email = fieldSet.readString("email")
)
}
})
.build()
}
处理固定长度文件
kotlin
@Bean
fun fixedLengthReader(): FlatFileItemReader<Transaction> {
return FlatFileItemReaderBuilder<Transaction>()
.name("transactionReader")
.resource(ClassPathResource("data/transactions.dat"))
.fixedLength()
.columns( // [!code highlight] // 定义列范围
Range(1, 10), // 账号
Range(11, 30), // 账户名
Range(31, 42), // 交易金额
Range(43, 50) // 交易日期
)
.names("accountNo", "accountName", "amount", "date")
.fieldSetMapper { fieldSet ->
Transaction(
accountNo = fieldSet.readString("accountNo"),
accountName = fieldSet.readString("accountName"),
amount = fieldSet.readBigDecimal("amount"),
date = LocalDate.parse(fieldSet.readString("date"))
)
}
.build()
}
注意事项
- 文件编码问题:使用
.encoding("UTF-8")
显式指定编码 - 标题行处理:使用
.linesToSkip(1)
跳过CSV标题行 - 空值处理:使用
.includedFields(0,2,4)
选择特定列
2.3 FlatFileItemWriter - 文件写入器
基础写入配置
kotlin
@Bean
fun customerWriter(): FlatFileItemWriter<Customer> {
return FlatFileItemWriterBuilder<Customer>()
.name("customerWriter")
.resource(FileSystemResource("output/customers_export.csv"))
.delimited()
.delimiter("|") // [!code highlight] // 使用管道符分隔
.names("firstName", "lastName", "email")
.headerCallback { writer ->
writer.write("FIRST_NAME|LAST_NAME|EMAIL") // [!code highlight] // 写入标题行
}
.append(true) // 追加模式而非覆盖
.build()
}
自定义行聚合器
kotlin
@Bean
fun customFormatWriter(): FlatFileItemWriter<Transaction> {
return FlatFileItemWriterBuilder<Transaction>()
.name("transactionWriter")
.resource(FileSystemResource("output/transactions.txt"))
.formatted() // [!code highlight] // 格式化写入
.format("%-10s%-20s%12.2f%8s") // [!code highlight] // 格式化模板
.names("accountNo", "accountName", "amount", "date")
.lineAggregator { transaction ->
String.format("%-10s%-20s%12.2f%8s",
transaction.accountNo,
transaction.accountName,
transaction.amount,
transaction.date.toString()
)
}
.build()
}
3. 高级应用技巧
3.1 多文件处理模式
3.2 分隔符与引号处理
kotlin
.delimited()
.delimiter(";")
.quoteCharacter('"') // [!code highlight] // 处理带引号的内容
.escapeCharacter('\\') // 转义字符处理
3.3 大文件分块处理
大文件处理策略
kotlin
@Bean
fun chunkOrientedStep(): Step {
return stepBuilderFactory.get("bigFileStep")
.chunk<Customer, Customer>(1000) // [!code highlight] // 每1000条处理一次
.reader(customerReader())
.processor(customerProcessor())
.writer(customerWriter())
.faultTolerant()
.skipLimit(100) // 允许跳过100条错误记录
.skip(Exception::class.java)
.build()
}
4. 最佳实践总结
IMPORTANT
平面文件处理黄金法则
- 严格验证输入格式:使用
BeanWrapperFieldSetMapper
自动验证字段类型 - 资源清理:实现
ItemStream
接口确保资源正确关闭 - 异常处理:配置
skipPolicy
处理格式错误记录 - 性能优化:对大文件使用
chunk
处理并设置合适提交间隔
4.1 配置检查清单
kotlin
fun validateReaderConfig() {
// 确保所有配置项正确
reader.afterPropertiesSet() // [!code error] // 忘记调用会导致运行时错误
// 正确方式
val reader = customerReader()
reader.afterPropertiesSet() // [!code highlight] // ✅ 必须调用
}
4.2 注解配置 vs DSL配置
kotlin
@Configuration
class BatchConfig {
@Bean
fun reader() = FlatFileItemReaderBuilder<Customer>()
.name("reader")
// ... 其他配置
.build()
}
xml
<!-- 避免使用XML配置 -->
<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="file:data/input.csv" />
<!-- 更多冗长配置 -->
</bean>
平面文件处理是Spring Batch的核心能力之一,掌握这些技巧将使您能够高效处理各种数据交换场景。遇到复杂需求时,记住Spring Batch的扩展性设计允许您通过实现LineMapper
或LineAggregator
接口来自定义解析逻辑!