Careeroza
OneToOne
Order and Payment
Every ShopEase order has at most one payment. The table that holds the foreign key is the owning side. Here payments.order_id points to the order.
@Entity
@Table(name = "payments")
public class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id", unique = true) // unique makes it truly one-to-one
private Order order; // owning side
private BigDecimal amount;
private String gatewayRef; // e.g. Razorpay payment id
}
// in Order:
@OneToOne(mappedBy = "order", fetch = FetchType.LAZY)
private Payment payment; // inverse sideSharing the Primary Key with @MapsId
A cleaner one-to-one design: the payment uses the same id as its order. @MapsId copies the order id into the payment primary key, so there is no separate order_id column and no extra index.
@Entity
@Table(name = "payments")
public class Payment {
@Id
private Long id; // no @GeneratedValue: taken from the order
@OneToOne(fetch = FetchType.LAZY)
@MapsId // payments.id = orders.id (also the FK)
private Order order;
private BigDecimal amount;
}create table payments (
amount decimal(38,2),
order_id bigint not null, -- primary key AND foreign key
primary key (order_id)
);The Lazy OneToOne Trap
Note: @OneToOne and @ManyToOne are EAGER by default. Always write fetch = FetchType.LAZY. Even then, the inverse side (Order.payment with mappedBy) is loaded eagerly by Hibernate: the orders table has no column that tells Hibernate whether a payment exists, so it must query. Prefer mapping one-to-one only on the owning side, or use @MapsId and load the payment with paymentRepository.findById(orderId).