Key Takeaways
- Spring Data JPA simplifies interactions with JPA providers like Hibernate.
- You can manage entity relationships, such as one-to-many, with suitable JPA annotations.
- Spring Boot projects can be configured to access databases through properties files.
- Custom repository methods can enhance predefined CRUD operations.
Spring Data JPA makes it easier to work with JPA providers. A JPA provider is an object-relational mapping (ORM) tool that implements the JPA specification. The JPA specification defines how Java objects represent relational database tables.
Spring Data JPA is an abstraction for simplifying the use of JPA providers such as Hibernate. By using Spring Data JPA, you can avoid the boilerplate code associated with managing transactions and entity managers.
What's in this tutorial?
In this Spring Data JPA tutorial, see how to create a Spring Boot app that manages a one-to-many relationship between authors and books using Spring Data JPA and MySQL.
1. Creating the project
You can easily create a project with all the necessary dependencies using Maven.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
<groupId>mygroup</groupId>
<artifactId>bookservice</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
spring-boot-starter-data-jpa includes dependencies for Hibernate and Spring Data JPA abstractions.
mysql-connector-java includes the MySQL database driver needed to connect to your datasource.
2. Configuring the database
Spring provides abstractions that simplify the details of connecting to a MySQL instance. You configure these aspects in application.properties so Spring Data JPA knows how to work with your database:
src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/books_service
spring.datasource.username=<DATABASE USERNAME>
spring.datasource.password=<DATABASE PASSWORD>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform = org.hibernate.dialect.MySQLDialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
3. Create the entities
Entities are classes that map to tables in the database. Each table corresponds to an entity class:
Author Entity
src/main/java/bookservice/model/Author.javapackage bookservice.model;
import javax.persistence.*;
import java.util.Set;
@Entity
public class Author {
@Id
@GeneratedValue
private Long id;
private String firstName;
@Column(unique=true)
private String lastName;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
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 javax.persistence.*;
@Entity
public class Book {
@Id
@GeneratedValue
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;
}
}
The @Entity annotation signifies that a class maps to a table in the database. Class members map to database columns. For example, the author table will have first_name and last_name columns.
Default column names can be overridden through @Column. This annotation lets you specify options like unique constraints.
The @Id annotation marks a member as the primary key for the table. It's usually used with @GeneratedValue to manage ID generation automatically.
Managing relationships between entities
Using @OneToMany and @ManyToOne annotations helps define relationships between entities like authors and books. One author can have many books, so the book table has a foreign key pointing to the author table.
The @OneToMany annotation assigns books to an author. The mappedBy = "author" attribute refers to the owning field of the relationship. This setup means the @ManyToOne annotation appears on the author field in the Book class.
FetchType.EAGER vs FetchType.LAZY
When using a FetchType, specify how related data is loaded (eagerly or lazily). Lazy loading saves resources by loading associated records only when necessary. Eager loading is preferable if the data will always be accessed.
4. Create the repositories
Repositories interact with the database, implementing data access via Spring Data JPA interfaces like CrudRepository.
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);
}
Extending the CrudRepository interface suffices to implement a basic data access layer. Providing the entity and ID type <Author, Long> allows Spring Data to automatically create beans for CRUD operations on the database.
But how is this possible?
Specifying @EnableJpaRepositories (default in recent Spring versions) lets Spring automatically implement JPA repositories based on these interfaces, including methods like:
save()
findAll()
findById()
delete()
Your custom method signatures can further define operations:
Book findByTitle(String title);
Spring implements the necessary query logic based on the method signature.
5. Running the application
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) -> {
//create a new author
Author author = new Author("George", "Orwell");
//create a new book
Book book = new Book("1984");
//save author to db
authRepo.save(author);
//associate author with book
book.setAuthor(author);
//save book
bookRepo.save(book);
//read book from db with custom findByTitle
Book savedBook = bookRepo.findByTitle("1984");
//print title
log.info(savedBook.getTitle());
//print book author's full name
log.info(savedBook.getAuthor().getFullName());
};
}
}
CommandLineRunner is used to run code after @SpringBootApplication starts. A new author and book are created, and the author is associated with the book via:
book.setAuthor(author);
When the book is saved, the foreign key for author_id is populated. Using the findByTitle() custom method, you can retrieve the saved book and log the related author's name, thanks to eager loading.
FAQ
Why use Spring Data JPA?
Spring Data JPA reduces boilerplate code for database access and helps manage JPA providers effectively, making data persistence tasks simpler and more efficient.
What does @Entity do in JPA?
The @Entity annotation in JPA marks a class as an entity bean, mapping it to a table in the database, with its fields representing columns.
How does Spring Data JPA implement custom queries?
Spring Data JPA derives query methods from method signatures in repository interfaces, translating these into SQL automatically.
What is FetchType.LAZY?
A FetchType of LAZY loads data on demand, which can conserve resources and improve application performance by not retrieving associated data until it's accessed.
