使用MongoDB与Jakarta EE实现批处理:从客户分群到可扩展工作流
批处理解决了企业中的一个常见需求:可靠地将业务规则应用于大型数据集,而不依赖于单个用户请求。诸如重新计算客户细分、迁移记录、核对交易、更新价格或处理数百万个文档等任务,需要的不仅仅是基本的循环。应用程序必须高效地控制内存、处理故障、最小化不必要的写入,并将长时间运行的进程与交互式用户体验分离。
MongoDB 和 Jakarta Batch 可以很好地协同工作来满足这些需求。在本文中,我们将开发一个客户细分工作流,允许管理员调整 Bronze、Silver、Gold 和 Platinum 客户的消费阈值,预览影响,并启动批处理作业以仅重新分类必要的记录。我们的重点超越批处理 API,着眼于设计一个清晰、可扩展的工作流,将业务决策与受控的数据处理连接起来。
本教程介绍 Batch 模式,并演示如何使用 MongoDB 实现它。
在本教程中,您将:
- 为电子商务应用程序建模客户细分系统。
- 使用 Java 建模并与 MongoDB 交互。
- 使用 Jakarta Faces Flow 和 Jakarta Batch 构建批处理工作流,以预览并应用客户重新分类规则。
您可以在 GitHub 仓库中找到本教程中展示的所有代码:
先决条件
本教程需要:
- Java 21。
- Maven。
- 一个 MongoDB 集群。
- MongoDB Atlas(选项 1)
批处理将大量记录作为受控工作负载进行管理,而不是作为单个冗长的请求。工作负载不是在一个 Web 请求中处理所有数据,而是被划分为可重复的单元。典型的批处理管道读取项目、应用业务逻辑、将结果分组为块,并将这些块写入数据库。这种方法提供了用于处理检查点、故障、重试、过滤和可重启性的不同点,同时还控制内存使用和数据库写入。
在 Jakarta EE 中,Jakarta Batch 使用作业、步骤和面向块的执行来标准化此模型。一个作业由一个或多个步骤组成,而块步骤通常链接 ItemReader、ItemProcessor 和 ItemWriter。读取器提供数据,处理器应用业务规则,写入器持久化更新后的项目。在我们的示例中,Jakarta Faces 收集新的客户细分阈值,而 Jakarta Batch 管理与 HTTP 请求无关的重新分类 MongoDB 客户的长期运行任务。 [LOADING...]
本教程使用一个实际的电子商务场景,涵盖使用 Jakarta EE 和 MongoDB 进行批处理。我们将展示 Jakarta Batch 如何跨许多记录管理操作,同时保持简单、响应迅速的用户体验。具体来说,我们将构建一个客户仪表板,根据可配置的消费阈值将用户分为 Bronze、Silver、Gold 和 Platinum 类别。
从仪表板中,管理员可以查看客户分布、调整类别阈值、预览更改的影响并启动批处理过程。然后,Jakarta Batch 将从 MongoDB 读取数据、应用新规则,并仅更新类别发生变化的客户,从而重新分类客户。
步骤 1:生成项目
本教程的第一步是创建初始 Maven 结构。您可以使用 Eclipse Starter for Jakarta EE。有关此步骤的更多详细信息,请参阅上一教程。
我们将在 pom.xml 中包含 MongoDB 驱动程序和 PrimeFaces。您可以在此处看到最终结果:
下一步是更新数据库中的初始配置,在 src/main/resources/META-INF/microprofile-config.properties 中,您将包含数据库配置和属性。
数据库配置完成后,我们现在可以创建 Customer 实体。该实体将包含以下属性:id、name、totalSpent 和 tier。tier 属性将使用枚举。
Java 和 MongoDB 使用 Jakarta Data Repository 进行通信,它提供了一个简单的集成接口。无需额外配置。
定义策略之后,CustomerSegmentationService 充当批处理操作的入口。它的主要作用是接收选定的策略,将其序列化为作业参数,并指示 Jakarta Batch 运行时启动 customer-segmentation 作业。
作业启动后,分块处理中的第一个批处理组件是 ItemReader。它一次向批处理运行时提供一个条目。在本应用中,CustomerItemReader 从仓库中检索客户,按客户 ID 排序,并通过 readItem() 顺序提供这些客户。
该读取器还演示了检查点(checkpointing),这是 Jakarta Batch 的一个关键概念。checkpointInfo() 返回当前位置,使运行时能够在分块边界处跟踪进度,并在需要时恢复执行。在本演示中,读取器将所有客户加载到内存中。对于更大的数据集,可以在不改变 Reader -> Processor -> Writer 模型的前提下,采用分页或基于游标的访问方式进行调整。
ItemProcessor 负责业务转换。Jakarta Batch 将读取器提供的每个客户发送给 CustomerTierProcessor,后者使用作业启动时提供的策略计算合适的等级。Jakarta Batch 通过 @BatchProperty 将策略作为序列化后的作业参数注入,并在处理器初始化时重新构造它。对于每个客户,处理器会比较计算出的等级与现有等级。如果等级未变,则返回 null 以过滤掉该条目。如果等级发生变化,则返回一个带有更新分类的新客户对象。
最后一个组件是 ItemWriter。与处理单个条目的读取器和处理器不同,写入器接收当前分块中累积的所有条目。在本应用中,这些条目仅是分类发生变化的客户,因为处理器会过滤掉未变化的客户。
CustomerItemWriter 将持久化工作委托给 CustomerRepository.saveAll(),使变更后的客户能够一起写入。这将持久化关注点从处理器中分离出来,并维持了 Jakarta Batch 模型中预期的职责分离。
最后一个元素是作业定义。Jakarta Batch 使用 JSL XML 描述批处理组件如何连接以及运行时应如何执行它们。在本示例中,customer-segmentation 作业包含单个步骤,以每 20 个条目为一个分块处理客户。读取器提供客户,处理器应用分层策略,写入器持久化变更后的记录。作业定义将阈值传递给处理器,让执行架构保持声明式,同时业务逻辑仍保留在 Java 中。
位于 resources/META-INF/batch-jobs/customer-segmentation.xml 的 XML 充当批处理管道的编排层。它不包含分层规则;相反,它连接各个组件并定义执行行为。这使得职责划分清晰:Java 类实现读取、处理和写入逻辑,而 JSL 文件定义这些组件如何参与作业。
下图展示了批处理工作过程和处理流程:
CustomerSegmentationService启动customer-segmentation作业,并提供序列化后的阈值作为作业参数。- Jakarta Batch 加载 JSL 定义并启动
recalculate-customer-tiers步骤。 CustomerItemReader检索客户并将其逐个提供给批处理运行时。CustomerTierProcessor处理每个客户,并根据配置的阈值确定合适的等级。- 如果等级保持不变,处理器返回 null,将该客户从写入阶段排除。
- 如果等级发生变化,更新后的客户将被包含在当前分块中。
- 作业配置了
item-count="20",因此 Jakarta Batch 会在调用写入器写入该分块之前,最多处理并累积 20 个变更条目。 CustomerItemWriter在写入阶段将变更后的客户保存到 MongoDB。- Jakarta Batch 对每个分块重复读取、处理和写入循环,直到所有客户处理完毕。
[LOADING...]
步骤 3:创建 Bean
下一步是将批处理流程连接到 Web 层。首页使用 @ViewScoped 的 Bean,因为其状态仅在仪表板视图活跃时需要。DashboardBean 通过 DashboardService 初始化仪表板状态,将其暴露给页面,并提供 refresh() 方法,使 UI 能够获取最新的客户分布和批处理状态,而无需将这些状态存储在用户会话中。
分层向导遵循不同的生命周期。Jakarta Faces 提供了 @FlowScoped,用于多步骤交互场景——状态需要在多个视图之间保持,但在用户离开流程后应被丢弃。在这里,CustomerSegmentationFlow 在管理员依次经过 Configure(配置)、Preview(预览)和 Review(审查) 时保留阈值配置和预览状态,并且仅在最终确认后才启动批处理。
该流程定义在 src/main/webapp/customer-segmentation/customer-segmentation-flow.xml 中。该文件指定了向导的导航方式:configure 是入口点,随后是 preview 和 review。返回首页节点用于退出流程并重定向到仪表板。
关键概念在于两种生命周期之间的区别:
@ViewScoped为当前页面保持仪表板状态。@FlowScoped跨多个页面保持向导状态。- 批处理执行独立于这两种 UI 作用域运行。
这种分离确保了 Web 交互保持短生命周期,而批处理作业在流程返回首页后仍继续处理。## 第 4 步:启动应用程序并使用它
要运行应用,请在本地启动 MongoDB,或配置 MongoDB Atlas 连接。使用提供的 Maven 和 GlassFish 设置构建并启动 Jakarta EE 应用。应用启动后,在浏览器中打开仪表板。如果客户集合为空,示例数据会自动加载,使你能够立即测试客户细分流程。
在仪表板上,选择 开始分段,并更新每个客户层级的最低消费金额。继续进入预览步骤,比较当前分布和预测分布。查看更改后,确认启动 Jakarta Batch 作业。处理期间应用会返回仪表板;作业完成后,刷新后的仪表板会显示更新后的客户分布。
[LOADING...]
结论
批处理提供了一种有条理的方法,可以独立于单个请求或用户交互来管理大规模流程。通过将工作负载划分为更小、可管理的单元,并以可预测的方式处理它们,运行时可以协调执行、监控进度并处理恢复。这种方法对于重复性的企业任务尤其有效,例如重新分类、对账、迁移和批量更新。
分块是批处理中的一个核心概念。它不是一次性处理整个数据集,而是将工作划分为更小的单元,以便增量处理和提交。最佳块大小因数据量、处理成本、内存和数据库性能而异。批处理为平衡这些权衡提供了清晰的结构,同时保持简单、可控的业务工作流。
准备好探索 MongoDB Atlas 的优势了吗?现在就开始,试用 MongoDB Atlas。
访问源代码,该源代码用于本教程。
有任何问题吗?来 MongoDB 社区论坛 与我们交流。
参考资料: