Quick Start | Spring Data JPA


This quick start guide will show you how to configure a Spring Data JPA project using Maven. If you are new to Spring Data JPA, we recommend checking out this Spring Data JPA intro first.

Quick Start Using Maven

1) Including Maven Dependencies in pom.xml

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 https://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>2.4.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>spring-data-jpa-examples</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-data-jpa-examples</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

This includes all of the libraries you'll need to work with Spring Data JPA including:

spring-boot-starter-data-jpa: Includes libraries for Hibernate, the JPA specification, JDBC, and spring-data-jpa itself.

mysql-connector-java: This is required dependency for MySQL implementation. It's the MySQL connector library for Java.

spring-boot-starter-test: This includes libraries for testing Spring Boot applications.

2) Installing Maven Dependnecies

To install the dependencies listed in the pom.xml simply run:

mvn install

To package the application into an executable file run:

mvn package

3) Add Entities

Entities are classes you define that represent tables in the MySQL database. Let's create an authors entity and a books entity. This will help with relationship examples (OneToMany, ManyToMany):

Author.java

package com.example.springdatajpaexamples;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
public class Author {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;
    private String name;
    protected Author() {}
    public Author(String name) {
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Book.java

package com.example.springdatajpaexamples;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Integer id;
    private String title;
    protected Book() {}
    public Book(String title) {
        this.title = title;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

@Entity tells JPA this class represents a table in the database. The class members(id, name) represent columns in the database.

@Id tells JPA this is the primary key for this table.

@GeneratedValue specifies how the primary key gets generated for new entries. GenerationType.AUTO is the default strategy. This strategy delegates to the JPA provider (Hibernate) for optimally generating unique ids.

4) Add Repositories

By simply extending JPA Repository interfaces, Spring Data JPA provides methods for accessing the database:

AuthorRepository.java

package com.example.springdatajpaexamples;
import org.springframework.data.repository.CrudRepository;
public interface AuthorRepository extends CrudRepository<Author, Integer> {
}

BookRepository.java

package com.example.springdatajpaexamples;
import org.springframework.data.repository.CrudRepository;
public interface BookRepository extends CrudRepository<Book, Integer> {
}

By simply defining interfaces for our entities, Spring Data automatically manages collection items for the corresponding tables.

Confused? Check out our starter Spring Data JPA guide for in depth explanations.

5) Configure application.properties

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/example
spring.datasource.username=root
spring.datasource.password=

This defines connection details for our MySQL instance. You'll notice we are connecting to a local instance of MySQL for this example.

6) Run the example

SpringDataJpaExampleAmmplication.java

package com.example.springdatajpaexamples;
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;
import java.util.Optional;
@SpringBootApplication
public class SpringDataJpaExamplesApplication {
	private static final Logger log = LoggerFactory.getLogger(SpringDataJpaExamplesApplication.class);
	public static void main(String[] args) {
		SpringApplication.run(SpringDataJpaExamplesApplication.class, args);
	}
	@Bean
	public CommandLineRunner demo(AuthorRepository authorRepo, BookRepository bookRepo) {
		return (args) -> {
			Author author = new Author("Sam Erickson");
			authorRepo.save(author); //saves author to database
			Optional<Author> result = authorRepo.findById(author.getId()); //find one from db
			if(result.isPresent()) {
				System.out.println(result.get().getName());
			}
		};
	}
}

Using the CommandLineRunner, we can test our entity classes.

PLEASE NOTE: Spring Data transactions work best inside @Service and HTTP Requests. Since Spring Data automatically handles Hibernate sessions, entity managers, etc. you can get unexpected results when not running Spring Data inside Spring Application Context.

Spring Data JPA Examples

findOne()

Note* The findOne() method was replaced by the findById() in newer version of Spring Data CrudRepository implementation. While you can still use findOne() method, it has been repurposed for QueryByExample querying technique. As a result, findById() is the preferred method for retrieving a single entity from the database:

Author author = new Author("Sam Erickson");
authorRepo.save(author); //saves author to database
Optional<Author> result = authorRepo.findById(author.getId()); //find one from db
if(result.isPresent()) {
	System.out.println(result.get().getName());
}

Notice how the authorRepo returns an Optional. For this reason, we check to make sure the result returned something via result.isPresent(). We then unwrap the optional via get().

Join Examples

A JOIN table combines two or more tables based on related columns. JOIN tables are used in cases where an author has many books and/or a book has many authors...

@OnetoOne

To create a one to one relationship between Books and Authors (one author has one book), modify the entity classes for Book and Author:

Author.java

//
@OneToOne
private Book book;
public Book getBook(){
    return this.book;
}
public void setBook(Book book){
    this.book = book;
}
//

Book.java

//
@OneToOne(mappedBy= "book")
private Author author;
public Author getAuthor() {
    return this.author;
}
public void setAuthor(Author author) {
    this.author = author;
}
//

The @OneToOne annotation is used on both entities. Since the relationship must have an owner side, we specify mappedBy="book" on the Book entity. This results in the authors table having a column book_id.

To see the relationship in action:

Author author = new Author("Sam Erickson");
Book book = new Book("Mastering the Tech Interview");
bookRepo.save(book);
author.setBook(book);
authorRepo.save(author); //saves author to database
Optional<Author> result = authorRepo.findById(author.getId()); //find one from db
if(result.isPresent()) {
  Author savedAuthor = result.get();
  System.out.println(savedAuthor.getBook().getTitle());
}

@OneToMany

To create a one to many relationship (one author having many books), modify the Book and Author entities:

Author.java

//
@OneToMany(mappedBy="author")
private Set<Book> books = new HashSet();
public Set<Book> getBooks(){
    return this.books;
}
public void setBooks(Set<Book> books){
    this.books = books;
}
//

Book.java

//
@ManyToOne
private Author author;
public Author getAuthor() {
    return this.author;
}
public void setAuthor(Author author) {
    this.author = author;
}
//

Since an author can have many books, we define a Set of books with the @OneToMany annotation. The mappedBy="author" indicates the book table will own the relationship.

To run the example...

Author author = new Author("Sam Erickson");
Book book = new Book("Mastering the Tech Interview");
Book book2 = new Book("My Second Masterpiece");
Set<Book> books = new HashSet();
books.add(book);
books.add(book2);
author.setBooks(books);
authorRepo.save(author); //saves author to database
bookRepo.save(book);
bookRepo.save(book2);
Author savedAuth = authorRepo.findById(author.getId()).get(); //find one from db
savedAuth.getBooks().forEach(b -> {
  System.out.println(b.getTitle());
});

This will ultimately create a author_id on the books table in the database.

@ManyToMany

To create a many to many relationship (many books having many authors) modify the Book and Author class with the following:

Author.java

//
@ManyToMany(mappedBy="authors")
private Set<Book> books = new HashSet();
public Set<Book> getBooks(){
    return this.books;
}
public void setBooks(Set<Book> books){
    this.books = books;
}
//

Book.java

@ManyToMany
@JoinTable(
        name = "BOOK_AUTHOR",
        joinColumns = @JoinColumn(name = "book_id", referencedColumnName="id"),
        inverseJoinColumns = @JoinColumn(name = "author_id", referencedColumnName="id")
)
private Set<Author> authors;
public Set<Author> getAuthors() {
    return this.authors;
}
public void setAuthors(Set<Author> authors) {
    this.authors = authors;
}

We specify the @ManyToMany annotation on both entities. Notice how we specify the @JoinTable only on the Book entity. This will generate a book-author table in the database.

To see the example in action...

Author author = new Author("Sam Erickson");
Author author2 = new Author("Ted Krinshaw");
Book book = new Book("Mastering the Tech Interview");
Book book2 = new Book("My Second Masterpiece");
Set<Author> authors = new HashSet();
Set<Book> books = new HashSet();
authors.add(author);
authors.add(author2);
books.add(book);
books.add(book2);
author.setBooks(books);
author2.setBooks(books);
book.setAuthors(authors);
book2.setAuthors(authors);
authorRepo.save(author);
authorRepo.save(author2);//saves author to database
bookRepo.save(book);
bookRepo.save(book2);
Author savedAuth = authorRepo.findById(author.getId()).get(); //find one from db
savedAuth.getBooks().forEach(b -> {
    System.out.println(b.getTitle());
});

Your thoughts?