PAYATHON 2026

Java LLD 中实体关联应使用对象引用还是 ID?数据应存在哪里?

支付老李

结论

Payment 可以保存 User 对象的引用,这对同一进程内的对象模型来说是合理的。是否改用用户 ID,取决于 Payment 是否需要独立加载、存储或跨接口传输。

当前需要调整的是字段命名:private User userID; 的类型是 User,保存的是对象引用,字段应叫 user。如果字段叫 userID,类型就应与 User.userID 一致,即 Long。

User、Payment 和 Flight 的集中存储与查询可以交给 Repository 或类似的数据访问组件。实体字段负责表达业务关系,不负责管理系统中的全部记录。

保存 User 引用意味着什么?

可以把 Payment 中的关联字段改成:

private User user;

把已有的 User 赋给这个字段时,Java 保存的是引用,不会自动复制整个 User。通过 Payment 可以访问关联的 User。如果多处引用同一个可变的 User 对象,一处的修改也可能被其他引用观察到。

这种方式适合在同一进程内直接访问用户信息。它本身不会把数据保存到数据库,也不会规定对象如何序列化。

如果使用 ORM,还需要配置对象关联如何映射到数据库。Java 字段的类型是 User,并不意味着数据库必须保存整个 User 对象。

什么时候更适合只保存 ID?

如果 Payment 主要需要记录这笔支付属于哪个用户,而用户信息由其他组件查询,保存 ID 通常更直接:

import java.util.UUID;

public class Payment {
    private UUID paymentID;
    private Long userID;
    private PaymentType paymentType;
    private Boolean isRefund;
    private double amount;
}

这里保留了原有字段,只调整了用户关联的类型。需要用户详情时,再通过 userID 查询 User。

可以参考以下场景选择:

场景可采用的设计
同一进程内,经常需要访问关联用户保存 User 引用
Payment 需要独立加载或存储保存用户 ID 是一种直接的方案
请求、响应或消息只需要标识用户传递用户 ID,或使用专门的 DTO

保存 ID 不会自动保证用户存在。创建 Payment 时应验证关联用户。如果使用关系数据库,也可以通过外键约束保证引用完整性。

先确定 User 与 Payment 的关系

原代码同时包含 User.payment 和 Payment.userID,相当于准备从两个方向维护关联。这需要保证两边始终一致,还可能导致 JSON 序列化时出现循环引用。

题目没有说明一个 User 可以有多少笔 Payment,因此还不能确定关系类型:

  • 如果一个用户只对应一笔 Payment,单个 payment 字段可能符合需求。
  • 如果一个用户可以有多笔 Payment,单个字段就不足以表达关系。
  • 如果只需要按用户查询支付记录,可以让 Payment 关联用户,再提供按 userID 查询的方法,不必在 User 中维护全部 Payment。

先确认业务上的数量关系,再决定是否需要双向关联。

User、Payment 和 Flight 应存在哪里?

如果 LLD 题目只要求实现进程内的功能,可以先用内存集合保存数据。下面是一个简化的存储容器,User 和 Payment 使用各自的 ID 作为键:

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class InMemoryStore {
    private final Map<Long, User> users = new HashMap<>();
    private final Map<UUID, Payment> payments = new HashMap<>();
}

Flight 可以采用相同思路,其 ID 类型需要根据设计确定。随着逻辑增多,可以拆成 UserRepository、PaymentRepository 和 FlightRepository,分别提供保存和查询方法。

内存集合中的数据通常会随进程结束而丢失,也不会自动在多个进程间共享。如果要求重启后保留记录,就需要数据库等持久化存储,由 Repository 封装相应的读写操作。

HashMap 适合这里的基础示意。如果存在并发访问,还需要设计线程安全和业务操作的一致性,不能仅靠增加几个 Map 字段解决。

两个字段也值得检查

amount 当前使用 double。如果它表示需要精确十进制计算的金额,应考虑使用 BigDecimal,或使用表示最小货币单位的整数,并明确币种和精度规则。

isRefund 使用 Boolean,可以是 true、false 或 null。如果业务只允许两种状态,可以使用 boolean;如果 null 有明确含义,可以保留 Boolean。

备注:内容仅供参考。