You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何识别数据库不存在的Access文件数据?

问题分析与解决方案

问题症状

Spring Boot应用读取Microsoft Access文件数据时,仅能识别数据库中已存在的记录并执行更新,无法识别新记录进行插入;且存在部分customId因测试日期不同重复的场景。

核心问题排查

  1. 参数绑定与查询逻辑问题:Repository的JPQL参数名与方法参数名未明确绑定,可能导致查询条件不匹配;
  2. 时间类型转换与精度差异:Access的DateTime字段读取后直接强制转换为LocalDateTime可能抛出异常,且Access时间可能包含毫秒,与数据库存储的时间精度不一致,导致查询匹配失败;
  3. 异步方法的事务与异常处理缺失:@Async方法默认无事务管理,且异常会被静默吞掉,导致部分记录处理失败;
  4. 字段类型转换不安全:直接强制转换Access字段值可能触发类型转换异常,跳过记录处理。

代码修正方案

1. Repository层修正

@Repository
public interface TestRepository extends JpaRepository<Test, Long> {
    
    @Query(value = "select t from Test t where t.customId = :customId and t.testDate = :testDate and t.testTypeId = :testTypeId")
    Optional<Test> findByTestParams(@Param("customId") String customId, 
                                   @Param("testDate") LocalDateTime testDate, 
                                   @Param("testTypeId") String testTypeId);
}
  • 使用Optional<Test>返回类型,避免空指针,同时用@Param明确参数绑定关系,防止Spring参数匹配错误。

2. Service层修正

@Service
@Slf4j
public class TestProcessingService {

    @Autowired
    private TestRepository testRepository;
    
    @Async
    @Transactional
    public void processData(Path filepath) throws IOException, SQLException {
        List<Test> testList = new ArrayList<>();
        Table table = DatabaseBuilder.open(new File(filepath.toString())).getTable("tblTests");
        
        for(Row row : table) {
            try {
                // 安全转换Access字段值
                String customId = String.valueOf(row.get("CustomID"));
                String testTypeId = String.valueOf(row.get("TestTypeID"));
                
                // 正确转换Access DateTime到LocalDateTime,截断毫秒保证精度一致
                Timestamp testDateTimestamp = (Timestamp) row.get("TestDate");
                LocalDateTime testDate = testDateTimestamp != null 
                    ? testDateTimestamp.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) 
                    : null;
                
                Float resultNumeric = null;
                if (row.get("ResultNumeric") != null) {
                    resultNumeric = ((Number) row.get("ResultNumeric")).floatValue();
                }
                
                Optional<Test> dbDataOpt = testRepository.findByTestParams(customId, testDate, testTypeId);
           
                if(dbDataOpt.isEmpty()) {
                    // 插入新记录
                    Test test = new Test();
                    test.setCustomId(customId);
                    test.setTestTypeId(testTypeId);
                    test.setTestDate(testDate);
                    test.setResultNumeric(resultNumeric);
                    testList.add(test);
                } else {
                    // 更新已有记录(仅更新需要变更的字段)
                    Test dbData = dbDataOpt.get();
                    dbData.setResultNumeric(resultNumeric);
                    testList.add(dbData); 
                }
            } catch (Exception e) {
                log.error("处理Access行数据失败,行内容: {}", row, e);
            }
        }
        testRepository.saveAll(testList);
    }
}
  • 添加@Slf4j日志注解,记录异常信息,避免数据处理异常被静默吞掉;
  • 修正DateTime类型转换逻辑,将Access返回的Timestamp转为LocalDateTime并截断毫秒,保证与数据库时间精度一致;
  • 安全转换字段值,避免类型转换异常;
  • 添加@Transactional注解,保证异步方法的事务一致性;
  • 优化更新逻辑,仅更新resultNumeric等可能变化的字段,减少不必要的字段赋值。

3. 额外注意事项

  • 确认Access驱动(如UCanAccess)版本与Spring Boot版本兼容;
  • 检查数据库中tbl_tests表的testDate字段精度,确保与代码中截断后的时间格式匹配;
  • 若存在大量数据,可考虑分批次调用saveAll优化性能。

内容的提问来源于stack exchange,提问作者Janeth Jackson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 06:48:14