Spring项目中Excel与数据库互导时多字段去重实现方案问询
看起来你正在搭建一个Excel和数据库双向同步的Spring服务,关于数据去重和导入导出的核心逻辑,我整理了一套实用的方案,你可以参考下:
核心解决方案与代码实现
一、先预加载数据库的唯一标识数据
要实现Excel数据的重复校验,首先得把数据库中已存在的ID、手机号、用户名这三个关键字段的数据捞出来,存到Set集合里做快速校验。
1. 数据库查询方法(Repository层)
在你的ContactRepository里添加一个查询方法,专门获取这三个字段的组合:
@Repository public interface ContactRepository extends JpaRepository<Contact, Long> { @Query("SELECT c.id, c.phone, c.username FROM Contact c") List<Object[]> findIdPhoneUsernameCombination(); }
2. 封装唯一键集合
在业务层写一个方法,把查询到的数据拼接成唯一键(用特殊分隔符避免不同字段组合冲突):
@Autowired private ContactRepository contactRepository; // 加载数据库中已存在的唯一标识组合 private Set<String> getExistingUniqueKeys() { List<Object[]> existingCombinations = contactRepository.findIdPhoneUsernameCombination(); Set<String> uniqueKeySet = new HashSet<>(); for (Object[] parts : existingCombinations) { String id = Objects.toString(parts[0], ""); String phone = Objects.toString(parts[1], ""); String username = Objects.toString(parts[2], ""); // 用|做分隔符,确保不同字段的组合不会重复 String uniqueKey = String.join("|", id, phone, username); uniqueKeySet.add(uniqueKey); } return uniqueKeySet; }
二、Excel读取阶段的双重去重校验
修改你现有的read方法,同时实现Excel内部数据去重和与数据库数据重复校验:
@Override public List<Contact> read(String filePath) throws IOException { List<Contact> validContactList = new ArrayList<>(); Set<String> dbUniqueKeys = getExistingUniqueKeys(); Set<String> excelInnerKeys = new HashSet<>(); // 用于记录Excel内已出现的组合 try (FileInputStream inputStream = new FileInputStream(new File(filePath)); XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { // XLS格式用HSSFWorkbook XSSFSheet sheet = workbook.getSheetAt(0); // 默认读取第一个工作表 // 跳过表头,从第2行开始(索引为1) for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { XSSFRow row = sheet.getRow(rowNum); if (row == null) continue; // 读取Excel行数据,封装成Contact对象(根据你的实际列顺序调整) Contact contact = new Contact(); contact.setId(Objects.toString(row.getCell(0), "")); contact.setPhone(Objects.toString(row.getCell(1), "")); contact.setUsername(Objects.toString(row.getCell(2), "")); // 其他字段读取... // 生成当前数据的唯一键 String currentKey = String.join("|", contact.getId(), contact.getPhone(), contact.getUsername()); // 双重校验:Excel内无重复 + 数据库中不存在 if (!excelInnerKeys.contains(currentKey) && !dbUniqueKeys.contains(currentKey)) { excelInnerKeys.add(currentKey); validContactList.add(contact); } else { // 可添加日志记录重复数据信息 log.warn("跳过重复数据:ID={}, 手机号={}, 用户名={}", contact.getId(), contact.getPhone(), contact.getUsername()); } } } catch (IOException e) { log.error("读取Excel文件失败", e); throw e; } return validContactList; }
注意:这里用Objects.toString()处理空值,避免NullPointerException;如果字段是数字类型,要做对应的类型转换(比如row.getCell(0).getNumericCellValue()后转字符串)。
三、数据库导入的兜底去重逻辑
为了避免并发场景下的重复插入(比如多个请求同时导入相同数据),必须加上数据库层面的约束和代码层面的冲突处理:
1. 数据库添加联合唯一索引
给contact表添加ID、手机号、用户名的联合唯一索引:
ALTER TABLE contact ADD UNIQUE INDEX idx_id_phone_username (id, phone, username);
这样即使代码校验漏了,数据库会直接抛出唯一约束异常,阻止重复数据插入。
2. 批量插入时处理冲突
用原生SQL实现批量插入,结合数据库的冲突处理语法(MySQL用ON DUPLICATE KEY UPDATE,PostgreSQL用ON CONFLICT DO NOTHING):
@Autowired private EntityManager entityManager; public void batchImportContacts(List<Contact> contactList) { String sql = "INSERT INTO contact (id, phone, username, email, create_time) " + "VALUES (?, ?, ?, ?, NOW()) " + "ON DUPLICATE KEY UPDATE id = id"; // 重复时不做更新,也可根据需求更新其他字段 int batchSize = 50; // 批量大小按需调整 for (int i = 0; i < contactList.size(); i++) { Contact contact = contactList.get(i); entityManager.createNativeQuery(sql) .setParameter(1, contact.getId()) .setParameter(2, contact.getPhone()) .setParameter(3, contact.getUsername()) .setParameter(4, contact.getEmail()) .executeUpdate(); if (i % batchSize == 0) { entityManager.flush(); entityManager.clear(); } } entityManager.flush(); entityManager.clear(); }
四、数据库导出Excel的基础实现
如果是从数据库导出数据到Excel,直接查询后用Apache POI写入即可(数据库已通过唯一索引保证数据不重复):
public void exportContacts(List<Contact> contactList, String exportPath) throws IOException { try (XSSFWorkbook workbook = new XSSFWorkbook(); FileOutputStream outputStream = new FileOutputStream(new File(exportPath))) { XSSFSheet sheet = workbook.createSheet("联系人数据"); // 写入表头 XSSFRow headerRow = sheet.createRow(0); headerRow.createCell(0).setCellValue("ID"); headerRow.createCell(1).setCellValue("手机号"); headerRow.createCell(2).setCellValue("用户名"); headerRow.createCell(3).setCellValue("邮箱"); // 写入数据行 int rowNum = 1; for (Contact contact : contactList) { XSSFRow row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(contact.getId()); row.createCell(1).setCellValue(contact.getPhone()); row.createCell(2).setCellValue(contact.getUsername()); row.createCell(3).setCellValue(contact.getEmail()); } workbook.write(outputStream); } catch (IOException e) { log.error("导出Excel失败", e); throw e; } }
内容的提问来源于stack exchange,提问作者andu
相关产品推荐
相关产品推荐

