Spring Data JPA Example
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.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;
}
}
@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.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 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());
};
}
}