Spring Data JPA Example

Spring Data JPA

Spring Data JPA Tutorial

Spring Data JPA Example

Spring Data JPA CRUD Example

Spring Data JPA vs Hibernate

Spring Data JPA Interview Questions

In this Spring Data JPA example, see how a one-to-many relationship is implemented between two relational tables book and author.

Entities

Think of entities as tables in your database. For each table you can create an entity class whose members represent the table's columns.

Author Entity

src/main/java/bookservice/model/Author.java
package 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;
    }

}

@Entity signifies the class represents a table in the database.

@Id signifies the member is the primary key for this table.

@GeneratedValue means the id should be automatically generated. This means you don't have to worry about generating the id yourself.

@Column adds optional metadata for a given entity member. Using @Column, you can specify name, implement constraints (unique, nullable, etc).

@OneToMany and @ManyToOne specify the association between the two tables. An owner of the association is defined via mappedBy = "author" pointing to the field on the owning side of the relationship (in this case Book).

What's FetchType?

A FetchType is also specified. While FetchType.EAGER loads associated records at the time data is accessed, FetchType.LAZY only loads associated records when they are explicitly accessed by the application.

FetchType.LAZY saves memory and processing but FetchType.EAGER makes sense if the associated data is always being used by the application.

Repositories

Repositories are the gateway to interacting with the database. By simply extending Spring Data JPA defined interfaces, you can quickly perform CRUD operations without boilerplate code.

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.java
package bookservice.repository;

import bookservice.model.Book;
import org.springframework.data.repository.CrudRepository;

public interface BookRepository extends CrudRepository {
    Book findByTitle(String title);
}

Extending the CrudRepository along with specifying the entity and id type <Book, Long> is enough to generate repositories. Spring automatically creates the beans from these interfaces as long as @EnableJpaRepositories is specified. This happens by default with newer versions of Spring Boot and no configuration is necessary.

Note you can also define your own query method signatures similar to findByTitle(String title). Spring Data JPA automatically implements the interface based on the signatures you provide.

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) -> {
            //create a new author
            Author author = new Author("JK", "Rowling");
            //create a new book
            Book book = new Book("Harry Potter");
            //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("Harry Potter");
            //print title
            log.info(savedBook.getTitle());
            //print book author's full name
            log.info(savedBook.getAuthor().getFullName());
        };
    }
}

Your thoughts?