Modeling database relationships correctly is essential for any backend application. In JPA (Java Persistence API), incorrect or sub-optimal entity mappings can lead to data inconsistency and performance issues.
There are 3 main types of relationships:
- One-to-One
- One-to-Many / Many-to-One
- Many-to-Many
Let's explore these relationships using a simple e-commerce domain model:
CustomerAddressCartProduct

- One-to-One: A customer has one cart, and a cart belongs to one customer.
- One-to-Many: A customer has multiple addresses.
- Many-to-One: Multiple addresses belong to one customer.
- Many-to-Many: A cart can have multiple products, and a product can be in multiple carts.
Defining the relationships through JPA annotations:
@OneToOne
In a one-to-one relationship, we must decide which table owns the relationship (stores the foreign key). Here, Cart entity (carts table) is the owning side because it has @JoinColumn.
@Entity
@Table(name = "carts")
public class Cart {
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "customer_id", nullable = false, unique = true)
private Customer customer;
}
The Customer entity is the inverse side. It has a reference to Cart using mappedBy.
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@OneToOne(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private Cart cart;
}
fetch defaults to EAGER. Set it to LAZY to prevent loading the associated entity unless explicitly accessed.
@OneToMany and @ManyToOne
In one-to-many/many-to-one relationships, the many side is almost always the owning side.
In this example, Address entity (addresses table) stores the foreign key from customers table:
@Entity
@Table(name = "addresses")
public class Address {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
}
The inverse side Customer has a reference to the list of addresses.
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private List<Address> addresses = new ArrayList<>();
}
@ManyToMany
Many-to-many relationships require a separate join table to hold the foreign keys from both entities.
@Entity
@Table(name = "carts")
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "cart_products",
joinColumns = @JoinColumn(name = "cart_id"),
inverseJoinColumns = @JoinColumn(name = "product_id")
)
private List<Product> products = new ArrayList<>();
}
The Product entity is the inverse side with reference to the list of carts:
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@ManyToMany(mappedBy = "products", fetch = FetchType.LAZY)
private List<Cart> carts = new ArrayList<>();
}
Unidirectional vs. Bidirectional Relationships
All the relationships demonstrated above are bidirectional. In JPA, relationships can be navigated in one or both directions:
- Unidirectional: Only one entity contains a field that refers to the other. For example, if
Customerhas aCartfield, butCartdoes not have aCustomerfield, the relationship is unidirectional fromCustomertoCart. - Bidirectional: Both entities have a field that refers to each other. This allows navigation from either side (e.g., finding the cart for a customer, or finding the customer for a cart). In a bidirectional relationship:
- The owning side is responsible for updating the relationship in the database. It holds the foreign key (using
@JoinColumn). - The inverse side uses the
mappedByattribute to point to the field in the owning entity that manages the relationship.
- The owning side is responsible for updating the relationship in the database. It holds the foreign key (using