ManyToMany
User Wishlist with a Join Table
A user can wishlist many products, and a product can be in many wishlists. The relationship lives in a join table with two foreign keys.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
@ManyToMany
@JoinTable(
name = "wishlist",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "product_id"))
private Set<Product> wishlist = new HashSet<>(); // owning side
public void addToWishlist(Product p) { wishlist.add(p); p.getWishlistedBy().add(this); }
public void removeFromWishlist(Product p) { wishlist.remove(p); p.getWishlistedBy().remove(this); }
}
// in Product (only if you need it):
@ManyToMany(mappedBy = "wishlist")
private Set<User> wishlistedBy = new HashSet<>();create table wishlist (
user_id bigint not null,
product_id bigint not null,
primary key (user_id, product_id)
);
-- adding product 7 to user 3's wishlist:
insert into wishlist (user_id, product_id) values (3, 7);Use Set, Not List
With a List (a "bag" in Hibernate terms), removing one product makes Hibernate delete every row of that user in the join table and insert the rest again. With a Set it deletes just one row.
Example
Removing one product from a wishlist of 3 products:
user.removeFromWishlist(earphones);Example output
-- with List<Product> delete from wishlist where user_id=? insert into wishlist (user_id,product_id) values (?,?) insert into wishlist (user_id,product_id) values (?,?) -- with Set<Product> delete from wishlist where user_id=? and product_id=?
Tip: Set needs a correct equals/hashCode on the entity. Base them on a natural key (like SKU) or on the id with a constant hashCode. The Hibernate & JPA advance level covers this in detail.
When the Link Has Data: Use an Entity
A cart is also "users ↔ products", but each link needs a quantity. @ManyToMany cannot hold extra columns. Model the link as its own entity with two @ManyToOnes. That is exactly what ShopEase's CartItem is.
@Entity
@Table(name = "cart_items",
uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "product_id"}))
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "product_id")
private Product product;
private int quantity;
private Instant addedAt;
}Tip: Many teams never use @ManyToMany at all: links usually gain extra data later (added date, quantity, role), and the link entity is easier to query and paginate.