In a Spring Boot application, the @OneToMany
annotation in JPA is used to define a one-to-many relationship between two entities. Let’s walk through an example where an Author
can have multiple Book
s.
📦 Project Setup
Add the following dependencies to your pom.xml
:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
🧱 Entities: Author
and Book
Author
Entity
java
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Book> books = new ArrayList<>();
// Getters and setters
public void addBook(Book book) {
books.add(book);
book.setAuthor(this);
}
public void removeBook(Book book) {
books.remove(book);
book.setAuthor(null);
}
// standard getters and setters
}
Book
Entity
java
import javax.persistence.*;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
// standard getters and setters
}
🗃️ Repositories
AuthorRepository
java
import org.springframework.data.jpa.repository.JpaRepository;
public interface AuthorRepository extends JpaRepository<Author, Long> {}
BookRepository
java
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {}
💼 Service Layer (Optional)
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AuthorService {
@Autowired
private AuthorRepository authorRepository;
public Author saveAuthorWithBooks(Author author) {
return authorRepository.save(author);
}
public List<Author> getAllAuthors() {
return authorRepository.findAll();
}
}
🧑💻 Controller
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/authors")
public class AuthorController {
@Autowired
private AuthorService authorService;
@PostMapping
public Author createAuthor(@RequestBody Author author) {
return authorService.saveAuthorWithBooks(author);
}
@GetMapping
public List<Author> getAuthors() {
return authorService.getAllAuthors();
}
}
⚙️ application.properties
properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
🧪 Sample JSON Request
To test your endpoint (POST /api/authors
), try this payload:
json
{
"name": "George R.R. Martin",
"books": [
{
"title": "A Game of Thrones"
},
{
"title": "A Clash of Kings"
}
]
}
✅ Summary
@OneToMany
is used on the parent entity (Author
) referencing the child (Book
)mappedBy
tells JPA which side owns the relationshipCascadeType.ALL
and orphanRemoval = true
allow full lifecycle management of children- Use
addBook()
and removeBook()
methods to keep both sides of the association in sync