This tutorial will demonstrate how One-to-One Mapping in Hibernate/Spring Boot Data JPA works. As a database, I will use a MySQL database server.
We will create two JPA entities: the Book and the Story. The Book and the Story entities have one to one relationship, which means that the Book entity has a Story entity, and the Story entity also contains a Book entity.
Because we will be using Spring Boot Data JPA, there is no need to create database tables manually. The framework will create all database tables based on how we annotate our JPA entities.
After running this example, you will be able to save the Book and the Story object in a MySQL database by sending an HTTP request using the Postman HTTP client.
If you are learning about Hibernate, you might also be interested in the following tutorials:
- Show Hibernate SQL Query in Spring Boot
- One-to-Many Mapping Hibernate/JPA Using Spring Boot and MySQL
- Many-to-Many Relationship in Spring Boot Rest +JPA
Default Fetch types
To implement the one-to-one relationship, we will use a special annotation called @OneToOne. This and other similar annotations have a default fetch type. I feel it is useful to mention their default values here.
@OneToOne – The default fetch type is EAGER.
@OneToMany – The default fetch type is LAZY.
@ManyToOne – The default fetch type is EAGER.
@ManyToMany – The default fetch type is LAZY.
One-to-One Mapping in Hibernate/JPA Spring Boot
Prerequisites:
Open eclipse and create a maven project; replace the pom.xml with the code below.
<?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.5.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.onetoonehibernatejpa</groupId> <artifactId>onetoone</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>onetoone</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>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Let maven download all necessary jars. Once it is done, you will be able to see the maven dependency folder that contains different jar files.
Now you can start writing our controller classes, ServiceImpl and Repository. The directory structure of the application looks as below.
Define JPA Entities: Book and Story
Book.java
package com.onetoonehibernatejpa.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonManagedReference; @Entity @Table(name = "book") public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId; @Column(name = "book_name") private String bookName; @OneToOne(cascade = CascadeType.ALL, mappedBy = "book") @JsonManagedReference private Story story; public Story getStory() { return story; } public void setStory(Story story) { this.story = story; } public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } }
Story.java
package com.onetoonehibernatejpa.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonBackReference; @Entity @Table(name = "story") public class Story { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int storyId; @Column(name = "story_name") private String storyName; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "book_id") @JsonBackReference private Book book; public int getStoryId() { return storyId; } public void setStoryId(int storyId) { this.storyId = storyId; } public String getStoryName() { return storyName; } public void setStoryName(String storyName) { this.storyName = storyName; } public Book getBook() { return book; } public void setBook(Book book) { this.book = book; } }
Define the Repository Interface Extending JPARepository.
BookRepository.java
package com.onetoonehibernatejpa.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.onetoonehibernatejpa.entity.Book; @Repository public interface BookRepository extends JpaRepository<Book, String> { public Book findByBookId(int bookId); }
Define service interface, i.e. BookService.java
package com.onetoonehibernatejpa.service; import org.springframework.stereotype.Component; import com.onetoonehibernatejpa.entity.Book; @Component public interface BookService { public Book saveBook(Book book); public Book findByBookId(int bookId); }
Define the Service Implementation Class
BookServiceImpl.java
package com.onetoonehibernatejpa.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.onetoonehibernatejpa.entity.Book; import com.onetoonehibernatejpa.entity.Story; import com.onetoonehibernatejpa.repository.BookRepository; import com.onetoonehibernatejpa.service.BookService; @Service public class BookServiceImpl implements BookService { @Autowired private BookRepository bookRepository; public Book saveBook(Book book) { Story story = book.getStory(); story.setBook(book); book = bookRepository.save(book); return book; } public Book findByBookId(int bookId) { Book book = bookRepository.findByBookId(bookId); return book; } }
Define the Controller class
BookController.java
package com.onetoonehibernatejpa.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.onetoonehibernatejpa.entity.Book; import com.onetoonehibernatejpa.service.BookService; @RestController @RequestMapping(value = "/book") public class BookController { @Autowired private BookService bookService; @RequestMapping(value = "/savebook", method = RequestMethod.POST) @ResponseBody public Book saveBook(@RequestBody Book book) { Book bookResponse = bookService.saveBook(book); return bookResponse; } @RequestMapping(value = "/{bookId}", method = RequestMethod.GET) @ResponseBody public Book getBookDetails(@PathVariable int bookId) { Book bookResponse = bookService.findByBookId(bookId); return bookResponse; } }
And finally, you have an application.properties file where you have database details.
application. properties
spring.jpa.hibernate.ddl-auto=create spring.datasource.url=jdbc:mysql://localhost:3306/db_test spring.datasource.username=test spring.datasource.password=test@123 spring.datasource.driver-class-name =com.mysql.jdbc.Driver #spring.jpa.show-sql: true
You are almost done. Just build the project once running the main method. Open cmd and run mvn clean install.
Let’s deploy the application running OnetooneApplication class as a java application.
Now you will prepare JSON data and try to save it in the database.
After running the example, you will have a database entry like the one below.
Test with Postman
You should now have records in the book and the story table. Notice book_id is the column in the story table is a foreign key. This is the primary key for the book table.
Test the get URL, i.e. http://localhost:8080/book/3
That’s all about One-to-One Mapping in Hibernate/JPA Spring Boot.