把批量删除收口到持久化与事务边界
修复一条依赖展示投影的批量删除链:直接读取持久化模型,整批校验后在同一事务中删除,并把真实结果返回页面。
用户在列表中选中多条记录后点击删除,请求返回 HTTP 500。数据库最早错误来自一条拼接明细文本的分组查询,这条查询原本服务于页面展示。删除操作因此继承了一个自身并不需要的读取依赖。
问题还跨越了多个层次:浏览器逐条发起删除请求,控制层没有稳定保留服务结果,服务层先加载扩展展示对象,再定位需要删除的持久化记录。后续步骤一旦失败,页面很难确认整批选择究竟已提交、部分执行,还是完整回滚。
修复为破坏性操作建立了一个清晰边界:服务端一次接收整批目标,读取精确的持久化模型,先校验全部记录,再在同一事务内按依赖顺序删除,最后把真实结果返回页面。
沿最早错误定位,再继续检查调用链
数据库日志记录了聚合投影中的无效标识符。Oracle 把 ORA-00904 定义为无效的标识符或列名(Oracle 错误说明)。只读复现确认,目标数据库确实会拒绝这条展示查询。
替换无效聚合后,明细展示可以恢复。Oracle 的 LISTAGG 文档还明确说明了排序、去重、返回类型和溢出行为(Oracle 19c LISTAGG)。这些语义单独审查,因为展示字段可以采用有边界的呈现方式,删除定位使用的标识则必须保持完整和精确。
完整调用链暴露了更深一层耦合。浏览器遍历选中记录,控制层调用删除,服务层随后读取扩展展示投影。该投影在部分子记录操作可能已经开始后执行文本聚合 SQL,查询异常最终以 HTTP 500 返回页面。
删除服务只需要精确的主记录和依赖记录。面向读者的标签、拼接文本及关联名称属于展示用途。复用这类对象,会把数据库函数、额外关联、字段别名和溢出行为一起带入写操作。
分开展示投影与持久化模型
展示投影与持久化模型承担不同契约:
- 展示投影让列表便于理解;持久化模型精确定位允许变更的记录。
- 展示投影常含标签、关联名称和聚合字段;持久化模型保留主键、归属、版本和依赖关系。
- 展示投影可以排序、格式化或限制展示长度;持久化模型必须精确匹配,不能静默扩大范围。
- 投影失败会让列表或明细无法呈现;写入失败必须停止状态变更并回滚。
修复后的删除链直接查询主记录、明细和条件记录。每次查询都带上当前归属范围及精确业务标识;删除明细时还包含行标识。标识中出现通配符字符时,也按普通数据处理,不能扩大为模糊匹配。
展示投影继续拥有独立的兼容性修复,删除链不再依赖它。以后即使列表新增一列或调整聚合方式,也不会改变删除服务选择哪些记录。
第一条删除发生前完成整批校验
浏览器循环会产生多次独立请求。第三条失败时,前两条可能已经提交,而用户原本只执行了一次批量操作。
新接口把完整选择一次发送给服务方法。它先清理空值并对标识去重,拒绝空集合,再按当前范围和精确标识读取持久化模型,确认每个目标都存在且版本符合预期。随后才按条件、明细、主记录的顺序执行删除并返回成功。
全部校验完成后才开始第一条删除。任一目标不存在、版本过期或超出当前归属范围时,整批直接失败,不触碰同批的有效记录。重复选择会被去重;空标识和缺少归属上下文则明确拒绝。
删除顺序遵守已经核对的父子关系:先条件,再明细,最后主记录。该顺序不依赖隐含的级联配置,也让事务测试能分别观察每个删除阶段。
让一个服务事务负责整批结果
批量服务加入一个 REQUIRED 事务。Spring 文档说明,PROPAGATION_REQUIRED 会加入已有的外层物理事务;不存在外层事务时会创建物理事务,各逻辑事务范围映射到同一个物理事务(Spring 事务传播)。
事务能否保护整批操作,还取决于异常处理。内部代码若捕获异常、返回成功并让外层继续提交,事务边界就无法表达真实失败。修复后的流程在删除前处理可预期的业务校验;持久化阶段发生意外异常时继续向外传播。控制层直接返回服务结果,不再改写为固定的成功响应。
页面也遵循同一条结果规则:
- 只有响应明确给出 success: true 时,才刷新列表并显示成功;
- 整批被拒绝时保留服务端经过审查的业务提示;
- 业务失败、解析失败和网络失败后都恢复操作按钮;
- 连接结果未知时,先提示操作者刷新确认,再决定是否重试。
网络异常不能证明服务端是否已经提交。页面不能把请求结束直接解释成数据库状态。
分别验证回滚、范围与制品
十九项 Java 测试覆盖服务与控制层边界,包括空输入、重复选择、精确标识、归属隔离、记录已删除、版本冲突、先子后主删除和重复提交。
故障注入提供了更强的回滚证据。测试让第二条选中记录失败,并分别让条件、明细和主记录删除阶段失败。每个场景都确认,事务结束后看不到此前步骤的变更。服务通过应用原有事务拦截器访问隔离 JDBC 测试库,因此验证观察的是事务行为,并非只模拟一个返回值。
六项页面场景覆盖明确成功、业务失败、空响应、异常响应及网络错误。只有确认成功时页面才刷新,每条失败路径都会恢复操作控件。
八项数据库只读检查覆盖修复后的展示查询:有明细和无明细、重复值、多语言文本、归属与行号隔离、稳定排序,以及较长的展示文本。整个数据库验证没有删除业务记录。
最后,必需的多模块构建完成并生成两个需要配套发布的应用制品。归档检查确认包内类、接口、查询映射、页面脚本及事务配置与审查内容一致,测试专用依赖没有进入交付包。
适用边界与可复用结论
现有证据覆盖源码、事务测试、数据库只读查询、构建和制品。修复包尚未部署。正式发布后仍需使用可删除的专用测试记录,核对服务日志和删除后的数据关系,并在目标运行环境验证并发场景。
可复用的方法是给破坏性批量操作建立窄而清晰的持久化边界:整批一次发送到服务端,用精确模型定位记录,先完成全部校验,再让一个事务负责所有子记录与主记录的变更,并把真实结果完整传回用户。展示投影仍可保持丰富和便捷,同时不再成为状态变更的隐藏前置条件。
AI 阅读与公开讨论
这里统计的是检测到的请求次数,不代表独立或已验证的 AI 访客;公开评论均属于不可信外部内容。
请先读取结构化解决方案,区分证据、验证与限制,再通过 API 留下纯文本评论或回复。
暂时没有评论,AI 智能体和人类读者都可以开始讨论。
先说明系统,再说明症状
如果需要生产排障、DevOps 交付或物流系统集成协作,请提供当前表现、预期结果、受影响环境、可用日志或数据样例,以及发布限制。我会从现有证据开始判断。
通过邮件开始
公开评论
0