Spring Boot + JPA/Hibernate Many to Many mapping example

Hello everyone, In this tutorial, we will learn how to implement many-to-many entity mapping using JPA/Hibernate with Spring Boot, Spring Data JPA, and the H2DB database.


Project Structure 

See the final project structure of this tutorial.


Maven[pom.xml] 

A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project. It contains default values for most projects. Some of the configurations that can be designated in the POM is the project dependencies, the plugins or goals that can be executed, the build profiles, and so on. Other information such as the project version, description, developers, mailing lists, and such can withal be designated.

<?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.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot_jpa_hibernate_manytomanymapping</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_jpa_hibernate_manytomanymapping</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</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>

application.properties

spring.datasource.url=jdbc:h2:mem:knfdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
spring.h2.console.enabled=true

Modal class[Author.class]

package com.knf.dev.model;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

@Entity
public class Author {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;

@ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY)
private Set<Book> books = new HashSet<>();

public Author() {
}

public Author(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public Author(String firstName, String lastName, Set<Book> books) {
this.firstName = firstName;
this.lastName = lastName;
this.books = books;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Set<Book> getBooks() {
return books;
}

public void setBooks(Set<Book> books) {
this.books = books;
}
}


The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys. The @ManyToMany annotation is used to implement a Many-to-Many relationship.

Model class[Book.class]

package com.knf.dev.model;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String isbn;
private String publisher;

@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "author_book", joinColumns =
@JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "author_id"))
private Set<Author> authors = new HashSet<>();

public Book() {
}

public Book(String title, String isbn, String publisher) {
this.title = title;
this.isbn = isbn;
this.publisher = publisher;
}

public Book(String title, String isbn, String publisher,
Set<Author> authors) {
this.title = title;
this.isbn = isbn;
this.publisher = publisher;
this.authors = authors;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getIsbn() {
return isbn;
}

public void setIsbn(String isbn) {
this.isbn = isbn;
}

public String getPublisher() {
return publisher;
}

public void setPublisher(String publisher) {
this.publisher = publisher;
}

public Set<Author> getAuthors() {
return authors;
}

public void setAuthors(Set<Author> authors) {
this.authors = authors;
}
}


The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys. The @ManyToMany annotation is used to implement the Many-to-Many relationship.@JoinTable annotation can be used in an association to customize the generated join table or to map the existing join table. @JoinColumn is used to specify a column for joining an entity association or element collection.

Repository[BookRepository.class]

package com.knf.dev.repository;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.knf.dev.model.Book;

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByIsbn(String isbn);

}


Spring @Repository annotation is used to indicate that the class provides the mechanism for storage, retrieval, search, update and delete operation on objects.

DTO[BookDto.class]

package com.knf.dev.dto;

import java.util.HashSet;
import java.util.Set;

public class BookDto {

private Long id;
private String title;
private String isbn;
private String publisher;

private Set<AuthorDto> authors = new HashSet<>();

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getIsbn() {
return isbn;
}

public void setIsbn(String isbn) {
this.isbn = isbn;
}

public String getPublisher() {
return publisher;
}

public void setPublisher(String publisher) {
this.publisher = publisher;
}

public Set<AuthorDto> getAuthors() {
return authors;
}

public void setAuthors(Set<AuthorDto> authors) {
this.authors = authors;
}
}

DTO[AuthorDto.class]

package com.knf.dev.dto;

public class AuthorDto {
private Long id;
private String firstName;
private String lastName;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public AuthorDto(Long id, String firstName, String lastName) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
}

Controller[HomeController.class]

package com.knf.dev.controller;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.knf.dev.dto.AuthorDto;
import com.knf.dev.dto.BookDto;
import com.knf.dev.model.Author;
import com.knf.dev.model.Book;
import com.knf.dev.repository.BookRepository;

@RestController
@RequestMapping("books")
public class HomeController {
@Autowired
BookRepository bookRepository;

@GetMapping
public List<BookDto> findAll() {

return bookMapper(bookRepository.findAll());

}

@PostMapping
public Boolean save(@RequestBody Book book) {
try {
bookRepository.save(book);
return true;
} catch (Exception e) {
System.out.println(e);
return false;
}

}

private List<BookDto> bookMapper(List<Book> book) {
List<BookDto> bookDto = new ArrayList<>();

for (Book bk : book) {
BookDto dto = new BookDto();
dto.setId(bk.getId());
dto.setIsbn(bk.getIsbn());
dto.setPublisher(bk.getPublisher());
dto.setTitle(bk.getTitle());
Set<AuthorDto> dtoAuthor = new HashSet<>();
for (Author auth : bk.getAuthors()) {
AuthorDto author = new AuthorDto(auth.getId(),
auth.getFirstName(), auth.getLastName());
dtoAuthor.add(author);
}
dto.setAuthors(dtoAuthor);
bookDto.add(dto);
}
return bookDto;
}
}


The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody.
@RequestMapping annotation maps HTTP requests to handler methods of MVC and REST controllers.

Main Driver Class[Application.class]

package com.knf.dev;

import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.knf.dev.model.Author;
import com.knf.dev.model.Book;
import com.knf.dev.repository.BookRepository;

@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
BookRepository bookRepository;

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Override
public void run(String... args) throws Exception {
// Insert dummy datas
Book book = new Book();
Set<Author> authorList = new HashSet<>();
Author author1 = new Author();
author1.setFirstName("Sibin");
author1.setLastName("knf");
authorList.add(author1);
book.setAuthors(authorList);
book.setIsbn("23423dfd");
book.setPublisher("knf");
book.setTitle("Java");
bookRepository.save(book);

}
}


The @SpringBootApplication annotation is a convenience annotation that combines the @EnableAutoConfiguration, @Configuration and the @ComponentScan annotations.

Run & Test 

Run Spring Boot application with the command: mvn spring-boot:run

Add New Book


Fetch All Book




Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete