
本文介绍如何基于 jackson csv 处理能力,实现对 csv 文件的“按 id 查重、删除旧记录、写入新数据”的原子化更新逻辑,避免简单追加导致的数据重复。
本文介绍如何基于 jackson csv 处理能力,实现对 csv 文件的“按 id 查重、删除旧记录、写入新数据”的原子化更新逻辑,避免简单追加导致的数据重复。
在使用 Jackson 的 CsvMapper 进行 CSV 读写时,其原生 API 不支持就地修改或行级删除——CSV 是纯文本格式,无法像数据库一样执行 DELETE WHERE id=...。因此,正确的做法是:全量读取 → 内存过滤 → 重建写入。核心思路是:先解析现有 CSV 为 Java 对象列表,根据请求中的 id 移除所有匹配旧行,再合并新增/更新对象,最后一次性覆写整个文件。
以下是完整的 addOrUpdate 实现(含健壮性处理):
public static void addOrUpdate(File file, List<empdata> newData) {
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(EmpData.class).withColumnSeparator(',');
// 1. 读取现有数据(跳过空文件或不存在文件)
List<empdata> existing = new ArrayList();
if (file.exists() && file.length() > 0) {
try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
MappingIterator<empdata> it = mapper.readerFor(EmpData.class).with(schema).readValues(reader);
existing.addAll(it.readAll());
} catch (IOException e) {
throw new RuntimeException("Failed to read CSV: " + file.getAbsolutePath(), e);
}
}
// 2. 按 id 去重:保留非冲突项 + 覆盖新数据(后到优先)
Map<integer empdata> mergedMap = new LinkedHashMap();
// 先加载原有数据(可能被后续覆盖)
for (EmpData emp : existing) {
if (emp.getId() != null) {
mergedMap.put(emp.getId(), emp);
}
}
// 再用新数据覆盖同 id 行(实现“删除+新增”语义)
for (EmpData emp : newData) {
if (emp.getId() != null) {
mergedMap.put(emp.getId(), emp);
}
}
// 3. 一次性覆写 CSV 文件(确保原子性 & 避免追加)
try (OutputStream os = new FileOutputStream(file)) {
ObjectWriter writer = mapper.writerFor(EmpData.class).with(schema);
writer.writeValues(os).writeAll(mergedMap.values());
} catch (IOException e) {
throw new RuntimeException("Failed to write CSV: " + file.getAbsolutePath(), e);
}
}</integer></empdata></empdata></empdata>
✅ 关键设计说明:
从 CSV 数据集生成图数据库加载器和三元组映射。将表格 CSV 数据转换为图数据库可直接使用的节点、边和三元组。
- 使用 LinkedHashMap 保证插入顺序,且天然支持按 id 覆盖旧值;
- 显式覆写(new FileOutputStream(file) 无 append=true)替代追加,彻底避免脏数据;
- 自动跳过空文件/首行标题(Jackson 默认将第一行视为 schema,只要 EmpData.class 字段与 CSV 列名严格对应即可);
- 异常明确包装,便于上层 API 统一错误处理。
⚠️ 注意事项:
- 若 CSV 文件极大(GB 级),需考虑流式分块处理或切换至数据库;
- 生产环境建议加文件锁(如 FileChannel.lock())防止并发写冲突;
- id 字段必须为 Integer/Long 等可判等类型,且 EmpData 需有 getId() 方法并确保非空校验;
- 首次运行时若文件不存在,会自动创建 —— 符合 RESTful “upsert” 语义。
通过该方案,原始示例中发送 [{ "id": 1, "name": "xyz", "gender": "female", "action": "update" }] 将自动移除原 id=1 行,并写入新记录,真正实现安全、幂等的 CSV 行级更新。










