Relationship Basics

Tables Use Foreign Keys, Objects Use References

In the database, an order item points to its order with a foreign key column (order_items.order_id). In Java, an OrderItem object simply holds a reference to an Order object. A relationship mapping tells Hibernate how to turn one into the other.

AnnotationMeaningShopEase example
@ManyToOnemany rows of this entity point to one row of the othermany Products belong to one Category
@OneToManyone row has a collection of rows of the otherone Order has many OrderItems
@OneToOneexactly one row on each sideone Order has one Payment
@ManyToManymany on both sides, through a join tablea User wishlists many Products, a product is in many wishlists

Unidirectional vs Bidirectional

  • Unidirectional: only one class knows about the other. Product has a category field, but Category has no list of products.
  • Bidirectional: both classes have a field. Order.items and OrderItem.order. You can navigate both ways, but you must keep both sides in sync.
  • The database is the same in both cases: one foreign key column. Bidirectional is a Java convenience, not a different table design.
Java objects                          Database tables
 ------------                          ---------------
 Order  ── items ──►  [OrderItem,...]  orders(id, user_id, status, total)
   ▲                        │                ▲
   └──────── order ─────────┘          order_items(id, order_id FK, product_id FK, quantity, price)

The Owning Side and mappedBy

In a bidirectional relationship there is still only one foreign key. JPA needs to know which Java field controls it. That field is the owning side. The other side says mappedBy = "fieldName" and is the inverse side: Hibernate ignores it when writing the foreign key.

  • For @ManyToOne / @OneToMany, the many side (OrderItem.order) is always the owning side.
  • For @OneToOne and @ManyToMany, you choose: the side without mappedBy owns it.
  • Only changes on the owning side are saved. Adding an item to order.getItems() without setting item.setOrder(order) stores order_id = NULL.

Note: mappedBy holds the Java field name on the other class (e.g. "order"), not a column name.

Practice

Exercise: In ShopEase, a CartItem belongs to one User and one Product. Which annotation goes on CartItem.user, and which side owns the relationship?

Show answer
// CartItem is the "many" side: many cart items per user.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;          // owning side: holds the cart_items.user_id foreign key

// Optional inverse side on User:
@OneToMany(mappedBy = "user")
private List<CartItem> cartItems = new ArrayList<>();