Java EE 企业应用 E05:两项真实 XA 资源如何在崩溃后定案
采购库提交了,外部结算库还停在 prepare 采购订单在数据库 A,供应商结算记录在数据库 B。两个普通 JDBC 连接分别 commit(),即使都写在一个 Java 方法里,A 成功、B 抛错时也无法撤销 A。把业务代码包上 @Transactional,如果连接本身不是可登记到同一事务协调器的 XA 资源,仍不能声称两个资源原子提交。本篇实验必须使用两个真正不同的资源管理器,例如两个隔离 PostgreSQL 16 实例、各自 XA-capable DataSource,由同一个 Jakarta Transactions 协调器管理;主线只有一个 jdbc/Procurement 普通数据源,至今没有第二库、XA 配置或协调器恢复日志,故所有 XA 路径 NOT_RUN。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 XA 提供的是协调两个事务分支的协议,不保证每一步都不会失败。对每一订单,应用先在 A 写 purchase_order 或其财务提交标志,在 B 写 suppl...
Java EE 企业应用 E02:邮件提交成功离收件人看到还差几步
SMTP 接受了一封“订单已生成”邮件,然后客户端超时 采购单生成后,系统要给申请人发通知。消费者调用 SMTP 发送,邮件服务器可能已回复成功,但客户端在读回复时断线。消费者只看到 I/O 错误,不知道服务器是否接收;若立刻无限重试,可能送出多封;若把这次记为“已送达”,实际 SMTP 服务器可能根本没有接受。此处有至少四种不同事实:订单业务事务已提交、outbox 任务已被消费者领取、SMTP 服务器接受消息、最终邮箱确实到达并可读。只有最后一项才能叫最终送达,而 Jakarta Mail 的一次发送返回不能保证这一点。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 001-initial.sql 只包含申请、明细和订单;后续 002/003 已有 outbox 与本地消费收据,但没有邮件收件人表、SMTP 发送尝试账本、SMTP 资源或本地邮件测试服务。主线第 23、30–31 篇的局部消息实验不等于邮件送达。本章没有在同名目录放模拟 SMTP 输出,实验均保持 NOT_RUN。...
Java EE 企业应用 E06:同一采购用例在 Jakarta EE 与 Spring Boot/MyBatis 的职责对照
换了技术栈,审批结果不能跟着换 采购申请 519 属于租户 A,状态 SUBMITTED,版本 7,总额 4900 元;主体只有 A 的 APPROVER 角色且限额 5000 元。审批后预期状态 APPROVED、版本 8,只形成一次审计事件。另一张跨租户或超额的申请不应因从 Jakarta EE 改用 Spring Boot/MyBatis 而变得可批;若事务回滚,审批和审计必须一起消失。这个例子是技术栈对照的固定输入,而不是本机已执行的受保护采购接口。现有 WAR 暂未接入可信 principal、审批额度和审计表;先修 04、09、12–23、35 的实际实验范围必须如实核对,不用后续章节的题目代替已经完成的代码。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 在 Jakarta EE 版本,HTTP 入口由 Servlet/REST 承接,容器创建 CDI 或会话 bean,用受管 DataSource 执行 JdbcRequestStore 的参数化 SQL;受管事务决定提...
Java EE 企业应用 E08:同一 WAR 在第二运行时能证明什么
原运行时 /api/health 正常,新运行时却找不到数据源 Open Liberty 的 server.xml 定义 jdbc/Procurement、PostgreSQL 驱动位置、主机端口、WAR 上下文根;迁移到第二个 Jakarta EE 运行时,把同一个 WAR 文件复制进去,不会自动复制服务器的资源定义。部署可能失败,也可能首次 getConnection() 才报错。即便健康端点返回 ready,它不查库、不验证认证或消息;不能把一张 HTTP 200 截图写成“采购审批系统已可移植”。E08 的先修是主线 00–39 的完整业务、权限、消息及交付验收;若先修不齐,应明确写未覆盖,而不是从早期 DataSource 探针外推全链等价。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 第二运行时已选 Eclipse GlassFish 8.0.4。writing-plans/javaee-enterprise/VERSIONS.md 记录该版的厂商 EE 11/JDK 2...
Java EE 企业应用 E07:`javax` 改名之前先锁定旧系统契约
WAR 在新容器报类找不到,订单 SQL 却完全没变 遗留采购应用由 Java EE 8 的 Servlet、REST、CDI 和事务 API 编译,打包后部署到使用 Jakarta EE 11 的运行时。一些源文件引用 javax.servlet.*,新容器的企业 API 在 jakarta.servlet.*;旧 WAR 即使有相同 URL 和 SQL,也不能据此推断二进制兼容。有人用全局字符串替换把 javax.sql.DataSource 改成 jakarta.sql.DataSource,编译立刻失败:JDBC 的 javax.sql 属于 Java SE,迁移企业 API 时不改包名。迁移验收应核对同一采购用例在旧/新环境的请求、事务、身份和失败契约,而非仅以编译结果定案。 本篇源码、迁移、脚本与测试可从固定版本完整归档取得;版本 20261005-local(基于 1b08ada),SHA-256 见源码清单。 E07 必须从独立旧工程开始:冻结 Java EE 8 应用服务器具体发行版、JDK 发行版/补丁、Maven 与每项 API 依赖,保存原始 WAR 哈希...
软件建模39:综合评审——怎样证明模型值得维护
一个模型通过当前预订场景,还不能说明它能够承受变化。延期会改变占用窗口,换设备会改变资源身份,追溯改价会改变历史金额的解释。三者都可能破坏“原请求重放仍返回原结果”这条看似独立的规则。 本篇对冻结的27-v1施加这三张变化卡,在旁边增加教学用的版本二状态层。原核心没有被修改,也没有把新功能倒写成既有应用的能力。每张卡都有实际成功路径、反例和保留事实的检查。 冻结基线再定义变化身份 初始快照有两台在役设备。原申请占用 E1 的十点到十一点,E1 另有十二点开始的租期,E2 则在十点半到十一点二十分被占用。所有时间采用 UTC,报价沿用每小时十二元五角,按小时向上取整。 27-v1规定请求号标识完整的原申请和费率,不能拿同一个号改租期或设备。因此修订需要新的预订请求号;变化操作还需要自己的 operationId,以识别一次延期、换设备或调价的重试。这两个身份解决不同层次的重复,不可互换。 版本二的 State 包含原应用快照、变化回执、金额调整记录和前后请求关系。收到相同操作号与相同负载时返回历史结果;同号不同负载直接拒绝。变化的原因和修订目标由显式命令传入,不从当前目录或现有活...
软件建模38:从领域模型到应用架构——哪些决定还没有作出
明确设备身份、半开租期和取消历史以后,应用仍然可以采用不同的代码组织与部署方式。领域模型没有决定请求从哪里进入、状态存在哪里、消息失败由谁处理,也没有决定需要几个进程。 本篇以冻结的27-v1为参照,执行事务脚本、领域实现和模块门面三种候选,再通过一次真实的本机 HTTP 请求贯穿入口、用例、仓储和发布。实验只在单个 JVM 中运行,逻辑边界与部署边界分别说明。 三个候选保持哪一段行为 共同轨迹包含首次确认、重叠拒绝、相邻确认、取消、晚时钟下重放旧确认、重放旧失败、未知设备、提前量不足,以及复用请求号却改变设备。比较对象包括每一步结果、最终活动数量、完整回执以及历史报价,不能只比较第一条请求返回的字符串。 事务脚本候选把判断集中在 reserve 中:读取设备数据,检查状态与租期,扫描当前占用,计算报价,保存活动记录和回执。它仍复用标识、租期与报价值规则,因此不是故意把所有基础类型退回字符串;主要差别是业务流程的判断顺序集中在一个过程里。 领域候选直接调用27的 RentalApplication,再由真实的设备、Specification、Factory 和 Reservat...
软件建模37:遗留系统建模——表和代码已经存在怎么办
遗留表中的 ready 字段为 Y,不足以证明设备能接受新的租赁。现有程序可能只检查 ready 与 busy,完全没有读取检验结果和退役标志。把表反向生成类图,会忠实展示这些字段,却不会指出旧判断遗漏了什么。 本篇从一份合成遗留 CSV 和真实执行的旧谓词出发,把设备逐批转换到冻结的27-v1应用。旧字段、未知编码和前后行为差异都保留在实验中。这里没有真实厂商数据,也没有已经获批的生产迁移方案。 从字段追到可观察行为 遗留记录包含 code、ready、busy、inspection、retired 和 note。旧查询的可租条件只有 ready=Y 且 busy=N,设备检验和退役状态不参与判断。四条样本都满足这个布尔条件,但分别表示检验通过、等待检验、已经退役以及未知检验码。 “旧程序这样计算”是一条可以运行验证的事实。“业务就应该这样决定”则是另一项判断。当前系列已经明确待检和退役设备不能新租,因此旧谓词在这两个样本上的放行属于需要改变的行为,不能以保持兼容为由永久保留。 教学输入约定 ready 表示维修侧已经放行,busy 只表示遗留系统记录了某种占用,inspec...
软件建模36:同题比较——分析模式、四色、DDD与FDD怎样配合
一份租赁需求可以同时借助分析模式辨认资源承诺,用四色区分参与角色与交易活动,用领域驱动设计确定一致性责任,再按 FDD 组织可交付特征。它们关注的问题存在重叠,不能通过“哪一张类图更短”评出统一名次。 本篇使用相同输入运行三个教学候选,检查它们分别遗漏什么。候选代表具体设计决定,不代表某种方法的全部能力;过程方法另用特征与验收结果的关联来评价。所有数据均为合成案例,没有真实团队工作坊或交付效率统计。 先固定需要解释的事实 申请 r1 希望租用 camera-A,时间为 UTC 十月三日十点到十二点。party-A 提出申请,party-B 承担付款角色。现行目录每小时十元,确认后更新为十五元。模型应能说明谁参与了这次活动、何时产生占用,以及哪一个价格仍然约束旧草案。 这组需求沿用半开区间:十二点结束的租期与十二点开始的租期相邻,不重叠。计划本身不占用设备;确认才产生承诺。取消释放占用,但原请求重试仍返回历史确认,不能重新占用;原来因冲突失败的请求,释放后重试也仍返回历史失败。 八个场景分别检查计划与承诺、重叠拒绝、相邻允许、取消后确认重放、失败重放、双重参与角色、旧费率保留和相...
软件建模35:函数式领域建模——类型、纯函数与显式状态变化
同一条租期规则可以写成对象方法,也可以写成接收数据、返回结果的函数。两种写法都必须拒绝提前量不足的申请,也必须解释拒绝后原有状态是否改变。把方法改成静态函数,并不会自动得到更可靠的领域模型;差别要落在输入、结果和状态变化的表达上。 本篇沿用 24:身份与值对象 的设备生命周期,以及 26:规则与工厂 的租期、报价和合同草案。实验用 Java 21 编译两组实现,逐项比较结果,再让两份有意写错的程序进入编译器。 相同规则,两种调用形状 教学规则保持不变:租期至少提前一小时,最长四十八小时;按实际时长向上取整到小时报价;费率版本和金额保存在草案中。设备可以从待检进入在役,从在役回到待检,待检或在役均可退役,退役后不能恢复。报价成功只表示生成草案,不能证明设备已经被预订。 对象方案直接编译既有的 Equipment、RentalPeriodSpecification、PricingService 和 RentalContract.Factory。Factory 持有规则对象与报价服务,调用 create 后返回草案,违反租期规则时抛出异常。设备对象则通过方法修改自己的状态;身份在操作...















