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:
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 >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-starter-parent < /artifactId >
< relativePath/ > < !-- lookup parent from repository -- >
< groupId > com. onetoonehibernatejpa < /groupId >
< artifactId > onetoone < /artifactId >
< version > 0 . 0.1 -SNAPSHOT < /version >
< packaging > war < /packaging >
< description > Demo project for Spring Boot < /description >
< java. version > 1.8 < /java. version >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-starter-data-jpa < /artifactId >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-starter-web < /artifactId >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-devtools < /artifactId >
< optional > true < /optional >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-starter-tomcat < /artifactId >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-starter-test < /artifactId >
< artifactId > mysql-connector-java < /artifactId >
< groupId > org. springframework . boot < /groupId >
< artifactId > spring-boot-maven-plugin < /artifactId >
<?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>
<?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 ;
@GeneratedValue ( strategy = GenerationType. AUTO )
@Column ( name = "book_name" )
@OneToOne ( cascade = CascadeType. ALL , mappedBy = "book" )
public Story getStory () {
public void setStory ( Story story ) {
public void setBookId ( int bookId ) {
public String getBookName () {
public void setBookName ( String bookName ) {
this . bookName = bookName;
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;
}
}
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 ;
@GeneratedValue ( strategy = GenerationType. AUTO )
@Column ( name = "story_name" )
private String storyName;
@OneToOne ( cascade = CascadeType. ALL )
@JoinColumn ( name = "book_id" )
public int getStoryId () {
public void setStoryId ( int storyId ) {
public String getStoryName () {
public void setStoryName ( String storyName ) {
this . storyName = storyName;
public void setBook ( Book book ) {
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;
}
}
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 ;
public interface BookRepository extends JpaRepository < Book, String > {
public Book findByBookId ( int bookId ) ;
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);
}
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 ;
public interface BookService {
public Book saveBook ( Book book ) ;
public Book findByBookId ( int bookId ) ;
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);
}
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 ;
public class BookServiceImpl implements BookService {
private BookRepository bookRepository;
public Book saveBook ( Book book ) {
Story story = book. getStory () ;
book = bookRepository. save ( book ) ;
public Book findByBookId ( int bookId ) {
Book book = bookRepository. findByBookId ( bookId ) ;
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;
}
}
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 ;
@RequestMapping ( value = "/book" )
public class BookController {
private BookService bookService;
@RequestMapping ( value = "/savebook" , method = RequestMethod. POST )
public Book saveBook ( @RequestBody Book book ) {
Book bookResponse = bookService. saveBook ( book ) ;
@RequestMapping ( value = "/{bookId}" , method = RequestMethod. GET )
public Book getBookDetails ( @PathVariable int bookId ) {
Book bookResponse = bookService. findByBookId ( bookId ) ;
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;
}
}
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
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
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.