One-to-Many Relationships
Real data is connected — one author has many books. JPA links entities with a couple of annotations.
What you will learn
- Model a one-to-many relationship
- Use @OneToMany and @ManyToOne
- Access related data as fields
Data is connected
Real apps link data together: one author writes many books; one customer has many orders. This is a one-to-many relationship. JPA models it with two annotations — one on each side.
| Side | Annotation | Meaning |
|---|---|---|
| The “one” (Author) | @OneToMany | One author has many books |
| The “many” (Book) | @ManyToOne | Each book belongs to one author |
The two entities
@Entity
public class Author {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "author") // one author -> many books
private List<Book> books = new ArrayList<>();
// constructors, getters, setters ...
}
@Entity
public class Book {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne // many books -> one author
private Author author;
// constructors, getters, setters ...
}Note: Output:
JPA adds an author_id column to the book table that points back to the author:
book(id, title, author_id)
The @ManyToOne side owns the link, so each book row stores which author it belongs to. mappedBy = "author" tells the Author side to use that same link.
Use the relationship
Once linked, you read related data like a normal field — no JOIN to write:
Author rowling = new Author("J.K. Rowling");
authorRepo.save(rowling);
Book book = new Book("Philosophers Stone");
book.setAuthor(rowling); // connect the two
bookRepo.save(book);
// later: read the author of a book, and the books of an author
System.out.println(book.getAuthor().getName());
System.out.println(rowling.getBooks().size());Note: Output: J.K. Rowling 1 The book knows its author, and the author knows its books — JPA loaded the related rows for you. You never wrote a SQL JOIN.
Tip: Naming helps: the side with @ManyToOne (here Book) holds the foreign-key column (a column that stores the id of a row in another table, linking the two — here author_id links each book to its author). The mappedBy on @OneToMany simply points to the field name on the other side that owns the link.
Watch out: Returning two entities that point at each other (author then books then author and so on) as JSON can cause an endless loop. A common fix is @JsonIgnore on one side, or returning a simpler DTO (Data Transfer Object — a small, plain class that carries only the fields you want to send, with no back-links). Keep this in mind when you serialise related data. (To serialise means to turn an object into text like JSON so it can be sent.)
Q. In a one-to-many relationship, which annotation goes on the “many” side (e.g. each Book)?
✍️ Practice
- Model a
Categorywith manyProductitems using@OneToManyand@ManyToOne. - Save a category, add two products to it, and print how many products it has.
🏠 Homework
- Model a
Coursethat has manyStudententities, link three students to a course, and list them.