Spring Data JPA Example
In this example, we'll walk through implementing a one-to-many relationship using Spring Data JPA, focusing on the relational tables book and author.
Key Takeaways
- Understand how to create entity classes for your database tables using JPA annotations.
- Implement common CRUD operations through Spring Data JPA repositories.
- Differentiate between FetchType.LAZY and FetchType.EAGER for entity associations.
Entities
Entities are Java classes that map to tables in your relational database. Each field in the class corresponds to a column in the table.
Author Entity
src/main/java/bookservice/model/Author.javapackage bookservice.model;
import jakarta.persistence.*; // Updated to use jakarta package.
import java.util.Set;
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // More explicit strategy.
private Long id;
private String firstName;
@Column(unique=true)
private String lastName;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author", cascade = CascadeType.ALL) // Added cascade for auto-saving books.
private Set books;
protected Author() {}
public Author(String first, String last) {
this.firstName = first;
this.lastName = last;
}
public Set getBooks(){
return this.books;
}
public String getFullName(){
return this.firstName + " " + this.lastName;
}
}
Book Entity
src/main/java/bookservice/model/Book.java
package bookservice.model;
import jakarta.persistence.*; // Updated to use jakarta package.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // More explicit strategy.
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
private Author author;
@Column(unique=true)
private String title;
protected Book() {}
public Book(String title){
this.title = title;
}
public String getTitle(){
return this.title;
}
public void setAuthor(Author author){
this.author = author;
}
public Author getAuthor(){
return this.author;
}
}
@Entity indicates that this class is a JPA entity, which maps to a database table.
@Id specifies the primary key in the table, with @GeneratedValue handling automatic key generation.
@Column is used to add constraints or change default mapping details for specific fields.
@OneToMany and @ManyToOne control the mapping between tables, with mappedBy indicating the inverse side of the relationship in the other entity.
Note the use of CascadeType.ALL: this automatically persists related entities in transactions, simplifying operations.
Repositories
Repositories abstract the data layer, allowing CRUD operations without boilerplate code by leveraging Spring Data JPA's predefined interfaces.
Author Repository
src/main/java/bookservice/repository/AuthorRepository.java
package bookservice.repository;
import bookservice.model.Author;
import org.springframework.data.repository.CrudRepository;
public interface AuthorRepository extends CrudRepository {
}
Book Repository
src/main/java/bookservice/repository/BookRepository.javapackage bookservice.repository;
import bookservice.model.Book;
import org.springframework.data.repository.CrudRepository;
public interface BookRepository extends CrudRepository {
Book findByTitle(String title);
}
By extending CrudRepository and specifying the entity and type parameters, Spring Data JPA generates implementations for standard CRUD operations. Further, custom query methods like findByTitle are automatically implemented based on method signatures.
Running the example...
Application.java
src/main/java/bookservice/Application.java
package bookservice;
import bookservice.model.Author;
import bookservice.model.Book;
import bookservice.repository.AuthorRepository;
import bookservice.repository.BookRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
private static final Logger log =
LoggerFactory.getLogger(Application.class);
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner demo(BookRepository bookRepo, AuthorRepository authRepo) {
return (args) -> {
Author author = new Author("JK", "Rowling");
Book book = new Book("Harry Potter");
// Save author which also saves books due to cascading.
book.setAuthor(author);
authRepo.save(author);
// Find and log the book details
Book savedBook = bookRepo.findByTitle("Harry Potter");
log.info(savedBook.getTitle());
log.info(savedBook.getAuthor().getFullName());
};
}
}
FAQ
What does @GeneratedValue(strategy = GenerationType.IDENTITY) mean?
This specifies the strategy for primary key generation, where GenerationType.IDENTITY relies on database auto-increment columns.
Why is @OneToMany using FetchType.LAZY?
This approach defers loading of related data, optimizing performance by only retrieving it when explicitly requested.
How does Spring Boot's autoconfiguration impact JPA setups?
Spring Boot's autoconfiguration simplifies JPA setups by automatically configuring necessary beans and dependencies, requiring minimal upfront configuration from developers.

