In this tutorial, we will learn how to implement step-by-step one-to-one entity mapping using JPA/Hibernate with Spring Boot, Spring Data JPA, and MySQL database
The @OneToOne JPA annotation is used to map the source entity with the target entity.
In this example, we create Instructor and InstructorDetail entities and we make a one-to-one mapping between them.
The one-to-one association can be either unidirectional or bidirectional.
In unidirectional association, the source entity has a relationship field that refers to the target entity and the source entity’s table contains the foreign key.In a bidirectional association, each entity (i.e. source and target) has a relationship field that refers to each other and the target entity’s table contains the foreign key. The source entity must use the mappedBy attribute to define the bidirectional one-to-one mapping.
In this example, we will implement step-by-step one-to-one unidirectional entity mapping with the Spring boot application.
Check out the bidirectional entity mapping example at JPA/Hibernate One to One Bidirectional Mapping Annotation Example.
Video
Tools and Technologies used
- Spring boot 3+
- Hibernate 3+
- JDK 17+
- Maven 3+
- IDE - STS or Eclipse
- Spring Data JPA
- MySQL 8+
Development Steps
- Create a Spring boot application
- Maven pom dependencies
- Project Structure
- Configuring the Database and Logging
- Defining the Domain Models
- Defining the Repositories
- Spring Boot Controller - InstructorController.java
- Run the application
1. Create a Spring Boot Application
There are many ways to create a Spring Boot application. You can refer below articles to create a Spring Boot application.
2. Maven Dependencies
Add required maven dependencies to pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.guides.springboot2</groupId> <artifactId>springboot-jpa-one-to-one-example</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.4</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3. Project Structure
4. Configuring the Database and Logging
Since we’re using MySQL as our database, we need to configure the database URL, username, and password so that Spring can establish a connection with the database on startup.
Let's open the src/main/resources/application.properties file and add the following properties to it -
spring.main.banner-mode=off
logging.pattern.console=%clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(%logger{0}){blue} %clr(%m){faint}%n
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
5. Defining the Domain Models
Instructor.java
package net.guides.springboot.jpa.model;
import jakarta.persistence.*;
@Entity
@Table(name = "instructor")
public class Instructor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "instructor_detail_id")
private InstructorDetail instructorDetail;
public Instructor() {
}
public Instructor(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
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 String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public InstructorDetail getInstructorDetail() {
return instructorDetail;
}
public void setInstructorDetail(InstructorDetail instructorDetail) {
this.instructorDetail = instructorDetail;
}
}
InstructorDetail.java
package net.guides.springboot.jpa.model;
import jakarta.persistence.*;
@Entity
@Table(name = "instructor_detail")
public class InstructorDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "youtube_channel")
private String youtubeChannel;
@Column(name = "hobby")
private String hobby;
public InstructorDetail() {
}
public InstructorDetail(String youtubeChannel, String hobby) {
this.youtubeChannel = youtubeChannel;
this.hobby = hobby;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getYoutubeChannel() {
return youtubeChannel;
}
public void setYoutubeChannel(String youtubeChannel) {
this.youtubeChannel = youtubeChannel;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
- @Table - maps the entity with the table. If no @Table is defined, the default value is used: the class name of the entity.
- @Entity - This annotation specifies that the class is an entity.
- @Id - declares the identifier property of the entity.
- @GeneratedValue - This annotation specifies the generation strategies for the values of primary keys.
- @Column - maps the entity's field with the table's column. If @Column is omitted, the default value is used: the field name of the entity.
- @OneToOne - The @OneToOne annotation is used to specify a one-to-one database relationship
6. Defining the Spring Data JPA Repository
Spring Data JPA contains some built-in Repository implemented with some common functions to work with a database.
Let's create the InstructorRepository interface which extends the JpaRepository interface.
InstructorRepository.java
package net.guides.springboot.jpa.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.guides.springboot.jpa.model.Instructor;
@Repository
public interface InstructorRepository extends JpaRepository<Instructor, Long>{
}
7. Spring Boot Controller - InstructorController.java
Let's create InstructorController class as Spring MVC controller and build CRUD REST APIs for Instructor resource:
package net.guides.springboot.jpa.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.guides.springboot.jpa.model.Instructor;
import net.guides.springboot.jpa.repository.InstructorRepository;
@RestController
@RequestMapping("/api/v1")
public class InstructorController {
@Autowired
private InstructorRepository instructorRepository;
@GetMapping("/instructors")
public List < Instructor > getInstructors() {
return instructorRepository.findAll();
}
@GetMapping("/instructors/{id}")
public ResponseEntity < Instructor > getInstructorById(
@PathVariable(value = "id") Long instructorId) throws ResourceNotFoundException {
Instructor user = instructorRepository.findById(instructorId)
.orElseThrow(() - > new ResourceNotFoundException("Instructor not found :: " + instructorId));
return ResponseEntity.ok().body(user);
}
@PostMapping("/instructors")
public Instructor createUser(@Valid @RequestBody Instructor instructor) {
return instructorRepository.save(instructor);
}
@PutMapping("/instructors/{id}")
public ResponseEntity < Instructor > updateUser(
@PathVariable(value = "id") Long instructorId,
@Valid @RequestBody Instructor userDetails) throws ResourceNotFoundException {
Instructor user = instructorRepository.findById(instructorId)
.orElseThrow(() - > new ResourceNotFoundException("Instructor not found :: " + instructorId));
user.setEmail(userDetails.getEmail());
final Instructor updatedUser = instructorRepository.save(user);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/instructors/{id}")
public Map < String, Boolean > deleteUser(
@PathVariable(value = "id") Long instructorId) throws ResourceNotFoundException {
Instructor instructor = instructorRepository.findById(instructorId)
.orElseThrow(() - > new ResourceNotFoundException("Instructor not found :: " + instructorId));
instructorRepository.delete(instructor);
Map < String, Boolean > response = new HashMap < > ();
response.put("deleted", Boolean.TRUE);
return response;
}
}
8. Run the application
package net.guides.springboot.jpa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import net.guides.springboot.jpa.model.Instructor;
import net.guides.springboot.jpa.model.InstructorDetail;
import net.guides.springboot.jpa.repository.InstructorRepository;
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private InstructorRepository instructorRepository;
@Override
public void run(String...args) throws Exception {
Instructor instructor = new Instructor("Ramesh", "Fadatare", "ramesh@gmail.com");
InstructorDetail instructorDetail = new InstructorDetail("Java Guides", "Cricket Playing");
// associate the objects
instructor.setInstructorDetail(instructorDetail);
instructorRepository.save(instructor);
}
}
9. Output
Conclusion
You might also be interested in checking out the following articles on JPA and Hibernate -
Get source code of this tutorial on my GitHub Repository.
Thanks!!
ReplyDeletethank you.
ReplyDelete