One-to-One and One-to-Many Relationships
One-to-One and One-to-Many Relationships
Real-world data is relational. Users have addresses, authors have books, orders have items. JPA provides annotations to map these relationships so you can navigate between entities in Java without writing JOIN queries yourself.
One-to-One: User and Address
A User has exactly one Address. In JPA, @OneToOne with @JoinColumn maps this to a foreign key column on one side of the relationship:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id")
private Address address;
// getters and setters
}
@Entity
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
private String zipCode;
// getters and setters
}
The @JoinColumn(name = "address_id") tells Hibernate which column in the users table holds the foreign key. CascadeType.ALL means when you persist or remove a User, the Address is persisted or removed automatically.
+------------------+ +------------------+
| users | | addresses |
+------------------+ +------------------+
| * id BIGINT PK | | * id BIGINT PK |
| name VARCHAR | | street VARCHAR |
| address_id FK | ------> | city VARCHAR |
+------------------+ | zipCode VARCHAR|
+------------------+
One-to-Many: Author and Books
An Author can write many Books. @OneToMany on the Author side and @ManyToOne on the Book side (the inverse side) maps this correctly:
@Entity
@Table(name = "authors")
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private List<Book> books = new ArrayList<>();
// getters and setters
}
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
// getters and setters
}
The mappedBy attribute on Author tells Hibernate that Book owns the relationship (the author_id foreign key lives in the books table). @ManyToOne on Book is the owning side. Always place @JoinColumn on the owning side.
+------------------+ +------------------+
| authors | | books |
+------------------+ +------------------+
| * id BIGINT PK | | * id BIGINT PK |
| name VARCHAR | <------ | title VARCHAR |
+------------------+ | author_id FK |
+------------------+
Cascade Types
Cascade types control what happens to related entities when you perform an operation on the parent. CascadeType.ALL cascades everything. Use CascadeType.PERSIST and CascadeType.MERGE if you want selective cascading. Avoid cascading REMOVE on large collections unless you truly want to delete all children.
Key Points
@OneToOnemaps a 1:1 relationship; use@JoinColumnfor the foreign key column.@OneToMany/@ManyToOnemaps a 1:N relationship; the Many side owns the foreign key.mappedByindicates the inverse (non-owning) side of the relationship.CascadeType.ALLpropagates all operations to related entities.- Always place
@JoinColumnon the owning side of the relationship.