一、为什么选“百万级 Excel 导入”而不是再测一遍 CRUDAI 写一个 Controller、Service、Mapper 并不难。真正能拉开 Java 代码生成质量差距的往往是需求里那些互相牵制的工程条件。这次我给两个模型的是同一份“百万级 Excel 商品数据批量导入”需求Spring Boot 3.2、Java 17、MyBatis-Plus 3.5、MySQL 8、EasyExcel 3.x要求按 Controller、Service、Mapper、Entity、DTO、VO 分层并同时处理流式读取、异步进度、逐行校验、分批事务、失败明细和文件幂等。它表面上是一个导入接口实际至少有六个坑全量加载会不会 OOM、上传结束后异步线程还能不能读到文件、单批失败会不会回滚之前批次、坏数据会不会中断整份文件、重复文件会不会并发插入两次以及生成的 SQL 是否贴合 MySQL 8。这类题目很适合验证方向3所关心的问题Java 专有模型与通用大模型面对同一个复杂 Java 需求时差异究竟出现在“写得快”还是“工程边界想得完整”。二、测试条件与证据边界实际 JDK 与操作系统版本不在本文展开。需要特别强调的是我这次记录了生成耗时并审查了关键代码但没有提供独立编译日志、自动化测试结果、真实百万行 Excel 运行记录或数据库压测报告。因此下面的“覆盖”“风险”和“更稳”均指截图中可复核的实现不代表完整生产验收。图1飞算JavaAI 3.9.8 专家模型中的测试 Prompt。图2Kimi-K3 中的同一份测试 Prompt。三、先看量化结果9 分钟 vs 15 分钟本次单次记录里飞算Java专家模型用时 9 分钟Kimi-K3 用时 15 分钟前者少用 6 分钟。以 Kimi-K3 的 15 分钟为基准计算(15 - 9) / 15 40%也就是说飞算Java在这一次同题生成中的耗时减少了 40%。这个差距是明确可感知的但样本量只有 1不能直接外推成“所有 Java 任务都快 40%”。生成长度、模型负载和工具侧输出方式都可能影响时间。更值得看的是 6 分钟之外的工程差异四、工程骨架两边都不只是给了一个 Service飞算Java输出了 Controller、Service、Mapper、Entity、DTO、VO、EasyExcel Listener、进度管理器、文件指纹工具、失败明细实体与 Mapper并给出了三张表商品表、导入任务表、导入失败明细表。接口侧也覆盖提交、进度和失败明细查询。图3飞算Java的模块结构失败明细与进度管理边界较完整。Kimi-K3同样给出了标准分层还额外拆出了ProductRowValidator、BatchSaver和ImportProgress。这三个边界很有价值校验可以独立测试监听器不直接绑定事务实现进度对象也能单独维护并发计数。图4Kimi-K3的工程结构校验器、批保存回调和进度对象拆分得比较清楚。仅看目录两边都理解了“这不是一个把List塞进 Mapper 的普通接口”。飞算Java更强调交付闭环Kimi-K3更强调核心职责拆分。五、关键代码对比OOM两边都抓住了“流式读取 有界批缓存”Kimi-K3的监听器把缓存上限固定为 1000 行达到阈值就落库并清空private static final int BATCH_SIZE 1000; private final ListProductImportRow buffer new ArrayList(BATCH_SIZE); buffer.add(row); if (buffer.size() BATCH_SIZE) { flushBuffer(); }飞算Java使用可配置的batchSize逻辑同样是逐行回调、达到阈值后批量写入private final int batchSize; private final ListProductImportDTO batchCache new ArrayList(); batchCache.add(data); if (batchCache.size() batchSize) { flushBatch(); }图5Kimi-K3监听器使用 1000 行缓存、文件内编码集合和批保存回调。图6飞算Java监听器包含字段校验、批缓存、失败明细持久化和进度更新。这两种实现都不会随着 Excel 总行数无限扩大“成功数据缓存”因此具备降低 OOM 风险的基础。飞算Java还把失败明细按批写进数据库避免失败列表无限增长Kimi-K3则在设计说明中将内存失败预览限制为 500 条。两种方式都在控制内存只是一个偏完整追溯一个偏轻量展示。这里仍不能写成“百万行一定不会 OOM”实体大小、EasyExcel 内部开销、数据库批次、失败率和 JVM 堆大小都没有经过实际压测。异步执行Kimi-K3的实现更稳飞算Java踩中了 Spring 代理边界Kimi-K3先将上传文件转存到临时路径再显式提交给线程池Path tempFile Files.createTempFile(excel-import-, - originalName); file.transferTo(tempFile); importExecutor.execute(() - doImportAsync(taskId, tempFile));这段代码解决了两个问题请求线程可以及时返回taskId异步任务读取的是自己管理的临时文件不依赖 Web 请求结束后MultipartFile的临时资源是否还存在。任务结束后它还在finally中删除临时文件。图7Kimi-K3显式使用线程池、临时文件和TransactionTemplate。飞算Java的截图则是this.executeImportAsync(taskId, file, fileMd5, fileName); Async(importExecutor) public void executeImportAsync(String taskId, MultipartFile file, String fileMd5, String fileName) { // 读取 file.getInputStream() }在 Spring 默认的代理模式下同一个对象内部通过this调用Async方法不会经过代理异步拦截器不会生效。Spring 官方文档也明确说明本地调用无法被默认代理模式拦截除非项目另外启用了截图中未展示的 AspectJ 编织否则这里很可能仍在请求线程同步执行。此外即使改成真正异步直接把MultipartFile交给后台线程也有文件生命周期风险。图8飞算Java的 Service 覆盖幂等、异步、进度和失败查询但Async自调用需要修正。这一组很有代表性飞算Java生成得更快、接口覆盖也更多但 Kimi-K3在最关键的异步边界上更接近可直接落地的写法。分批事务注解写出来不等于事务一定生效需求要求每批独立事务单批失败不能回滚已经成功的批次。Kimi-K3使用TransactionTemplatetransactionTemplate.executeWithoutResult(status - { for (int i 0; i products.size(); i UPSERT_CHUNK_SIZE) { ListProduct chunk products.subList(i, Math.min(i UPSERT_CHUNK_SIZE, products.size())); productMapper.batchUpsert(chunk); } });事务在回调执行时显式开启不依赖 Service 方法是否通过代理调用尤其适合监听器回调这种调用链。飞算Java在批量保存方法上使用了Transactional( propagation Propagation.REQUIRES_NEW, rollbackFor Exception.class ) public void batchUpsertWithIndependentTransaction( ListProduct products, String taskId) { productMapper.batchUpsert(products); }单看注解是正确方向但截图中监听器是在 Service 内部通过new ProductImportListener(..., this)创建并把当前 Service 的this传入监听器。监听器随后调用该对象的事务方法这仍可能绕过 Spring 代理。Spring 官方事务文档明确指出默认代理模式只拦截从代理外部进入的方法调用自调用不会创建预期事务。更稳妥的改法是把批写入单独拆成 Spring Bean让监听器持有代理 Bean或者直接采用TransactionTemplate。这一项我会把 Kimi-K3 判为工程实现更可靠而不是只看谁写了REQUIRES_NEW。行级失败飞算Java的“可追溯闭环”更完整两边都没有因为单行校验失败直接抛出异常终止整份文件。Kimi-K3将字段校验交给独立ProductRowValidator监听器遇到错误后记录行号和原因再继续下一行批量落库失败时它会把该批每行标记为失败。优点是监听器职责较轻缺点是失败明细主要保存在内存预览中截图显示最多保留 500 条Controller 也只展示了上传与进度两个接口。图9Kimi-K3提供上传接口和基于路径参数的进度查询。飞算Java把非空、长度、价格、库存和批内编码重复等校验直接写进监听器失败明细按批写入import_failure_detailController 还提供/failures查询入口。图10飞算Java除提交和进度外还生成了失败明细查询接口。从“最终给出成功/失败明细”的需求贴合度看飞算Java更完整重启后也能继续从数据库查询失败记录。不过它当前是一次性返回全部失败明细百万行高失败率场景还应补充分页或导出文件失败明细批量插入异常时只记录日志并清空缓存也可能造成明细丢失需要增加任务级告警或补偿。文件幂等Kimi-K3的数据库兜底更强Kimi-K3使用“MD5 原文件名 文件大小”组成文件指纹并在import_task_record.file_fingerprint上建立唯一索引。应用层先查询历史任务数据库唯一约束再承担并发兜底。这并不代表并发重复提交时的异常转换已经完整但至少不会静默插入两条相同指纹记录。飞算Java会计算文件 MD5也生成file_fingerprint但查询截图主要按file_md5筛选SUCCESS/PARTIAL_SUCCESSRUNNING状态的重复提交没有被同一查询覆盖。DDL 中file_md5只是普通索引file_fingerprint也没有展示唯一约束因此两个并发请求仍可能同时通过“未查到”的窗口。图11Kimi-K3在file_fingerprint上建立唯一索引。图12飞算Java生成商品、任务和失败明细三张表但文件幂等缺少唯一约束兜底。这一项不能只靠“先查再插”。建议飞算Java给规范化后的文件指纹建立唯一索引并显式处理唯一键冲突已完成则返回原taskId执行中则返回明确状态失败任务则按约定重试或新建重试记录。MySQL 8 upsertKimi-K3注意到了版本演进两边都使用商品编码唯一索引配合ON DUPLICATE KEY UPDATE满足“存在则更新、不存在则插入”的行级幂等目标。Kimi-K3的写法是VALUES (...) AS new_vals ON DUPLICATE KEY UPDATE product_name new_vals.product_name, category new_vals.category, price new_vals.price图13Kimi-K3使用新行别名引用待插入值。飞算Java的写法是ON DUPLICATE KEY UPDATE product_name VALUES(product_name), category VALUES(category), price VALUES(price)图14飞算Java同时生成 batchInsert、batchUpsert 和已存在编码查询但 upsert 使用了旧式VALUES()。MySQL 官方说明从 8.0.20 起在ON DUPLICATE KEY UPDATE中通过VALUES()访问新行值已被弃用推荐使用 8.0.19 起支持的行别名。因此Kimi-K3这一段更贴合 MySQL 8 的版本演进飞算Java当前写法通常仍可运行但会带来兼容性警告和未来迁移成本。六、双方优点、不足与适用场景飞算Java专家模型优点本次生成用时 9 分钟比 Kimi-K3 少 6 分钟单次耗时减少 40%。分层和交付覆盖更完整失败明细从实体、Mapper、表结构到查询接口形成闭环。行级字段校验较细进度、成功数、失败数和最终状态都有明确维护逻辑。生成结果明显带有 Java 后端工程意识不是只输出算法片段。不足Async被this自调用默认 Spring 代理模式下不会真正异步。异步方法直接使用MultipartFile缺少临时文件托管。REQUIRES_NEW事务方法可能因监听器持有当前对象this而绕过代理。文件幂等缺少数据库唯一约束运行中重复提交存在竞态窗口。MySQL 8 upsert 使用已弃用的VALUES()取值方式。适合场景希望快速获得较完整 Java 工程骨架、接口与持久化闭环再由熟悉 Spring 代理机制的开发者做一次关键链路审查。Kimi-K3优点显式线程池提交、临时文件转存和清理异步文件生命周期处理更稳。TransactionTemplate避开事务自调用问题批事务边界更明确。文件指纹唯一索引提供数据库级幂等兜底。MySQL 8 upsert 使用新行别名版本兼容意识更好。校验器、进度对象和批保存回调职责拆分清楚。不足本次生成耗时 15 分钟比飞算Java多 6 分钟。失败明细偏向内存预览截图未展示独立失败明细表与失败查询接口长期追溯能力较弱。批落库失败后把同一批所有行标成相同错误定位粒度仍可细化。唯一指纹能阻止重复数据但截图未完整展示并发唯一键冲突如何转换成友好业务响应。适合场景更看重核心异步、事务和数据库约束的可靠边界能够自行补齐失败明细持久化和接口闭环。七、如果要把两份代码推向生产我会先改什么对飞算Java版本我会优先做四项修改把异步导入拆到独立的 Spring Bean通过代理调用Async并在提交线程中先转存临时文件。把批写入拆到独立事务 Bean或改用TransactionTemplate再用故障注入验证“第二批失败不回滚第一批”。给文件指纹增加唯一索引并覆盖两个并发相同文件提交的测试。将 MyBatis XML 中的VALUES(column)改为新行别名写法。对 Kimi-K3版本我会优先补齐失败明细表、分页查询或失败文件导出同时为线程池拒绝、临时文件清理、唯一键冲突和高失败率场景增加测试。两边都还需要同一套验收编译与依赖检查、不同格式 Excel 解析、十万/百万行内存曲线、批次失败回滚、重复提交并发、进度一致性、任务重启恢复以及 MySQL 实际版本兼容验证。八、结论专有模型更快但“快”和“稳”不是同一个指标如果只看这次耗时飞算Java专家模型的优势很直观9 分钟对 15 分钟少用 6 分钟。它在失败明细、接口数量和数据表闭环上也更贴近一套完整 Java 后端模块确实体现出 Java 专有模型对工程结构的熟悉度。但逐行审查关键代码后结论并不是单方面胜负。Kimi-K3在异步文件托管、显式线程池、事务模板、文件指纹唯一约束和 MySQL 8 新语法上更稳飞算Java则出现了典型的 Spring 代理自调用问题。这些差异只有把注解背后的运行机制看进去才能发现。所以我对这次方向3实测的总结是飞算JavaAI 3.9.8 专家模型在生成速度和 Java 工程交付完整度上有可感知优势Kimi-K3在若干关键运行边界上更严谨。对开发者来说最好的用法不是盲选某一个模型而是让模型快速搭骨架再用编译、测试、并发与数据库约束去验证真正决定生产可靠性的部分。这次结果也提醒我AI 生成代码最有价值的评价指标从来不只是“看起来像一个完整项目”而是关键注解是否真的生效、失败路径能否恢复、并发时数据库能否守住最后一道边界。#飞算JavaAI #AI编程 #Java #Java代码生成 #AIcoding模型 #Java开发 #SpringBoot #MyBatisPlus #EasyExcel #MySQL