Spring Boot One to One Mapping Example - Spring Crud Example using One to One Mapping
Tags: springboot
Published: 2021-03-05 15:31:43

https://loizenai.com/spring-boot-one-to-one-mapping-example/

Tutorial: "Spring Boot One to One Mapping Example - Spring Crud Example using One to One Mapping"

In the tutorial, we show how to expose Crud RestAPIs <code>Post/Get/Put/Delete</code> to interact with Hibernate Spring JPA One-to-One relationship models using SpringBoot and PostgreSQL database.

<!--more-->
* Technologies - Spring Boot One to One Mapping Example:
- Java 8
- Maven 3.5.4
- SpringToolSuite version 3.9.4.RELEASE
- SpringBoot 2.0.4.RELEASE
<h2 id="overview-spring-boot-one-to-one-mapping-example">Overview - Spring Boot One to One Mapping Example</h2>
We have 2 models <code>Student</code> & <code>Contact</code> with <code>one-to-one</code> relationship:

[caption id="attachment_4771" align="alignnone" width="504"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-One-to-One-Model-Relationships.png" alt="Spring Boot One to One - Model Relationships - Spring Boot One to One Mapping Example" width="504" height="274" class="size-full wp-image-4771" /> Spring Boot One to One - Model Relationships[/caption]

We create a SpringBoot project as below:

[caption id="attachment_4772" align="alignnone" width="701"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-To-One-Postgresql-Architecture.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One To One Postgresql Architecture - Spring Boot One to One Mapping Example" width="701" height="325" class="size-full wp-image-4772" /> Spring Boot Rest Apis Hibernate Spring JPA One To One Postgresql Architecture[/caption]

[caption id="attachment_4773" align="alignnone" width="346"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Project-Structure.png" alt="SpringBoot Project Structure - Spring Boot One to One Mapping Example" width="346" height="450" class="size-full wp-image-4773" /> SpringBoot Project Structure[/caption]

Hibernate JPA configuration for 2 models <code>Student</code> & <code>Contact</code>:

- <code>Student</code> model ->

<pre><code class="language-java">
@Entity
@Table(name = "students")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
    
    @Column(name = "name")
	private String name;
    
    @Column(name = "age")
	private int age;
    
    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "student")
    private Contact contact;
	
	public Student() {}
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	// Getters & Setters methods
	// ...
</code></pre>

- <code>Contact</code> model:

<pre><code class="language-java">
@Entity
@Table(name = "contacts")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Contact implements Serializable{
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "city")
    private String city;
    
    @Column(name = "phone")
    private String phone;
    
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;
    
    public Contact() {
    }
    
    public Contact(String city, String phone) {
    	this.city = city;
    	this.phone = phone;
    }
	
	// Getters & Setters methods
	// ...
</code></pre>

We exposes RestAPIs for <code>Post/Get/Put/Delete</code> Students & Contacts:

- Students ->
<ul>
<li><code>@GetMapping("/api/students")</code>: get all Students</li>
<li><code>@GetMapping("/api/students/{id}")</code>: get a Student by ID</li>
<li><code>@PostMapping("/api/students")</code>: post a Student</li>
<li><code>@PutMapping("/api/students/{id}")</code>: update a Student</li>
<li><code>@DeleteMapping("/api/students/{id}")</code>: delete a Student</li>
</ul>

- Contacts ->
<ul>
<li><code>@GetMapping("/contacts")</code>: get all Contacts</li>
<li><code>@GetMapping("/students/{studentId}/contacts")</code>: get a Contact by Student's ID</li>
<li><code>@PostMapping("/students/{studentId}/contacts")</code>: add a Contact</li>
<li><code>@PutMapping("/contacts/{contactId}")</code>: update a Contact</li>
<li><code>@DeleteMapping("/contacts/{contactId}")</code>: delete a Contact by ID</li>
</ul>

Now we'll create a project from scratch. Let's go!
<h2 id="create-spring-boot-project">Create SpringBoot project - Spring Boot One to One Mapping Example</h2>
We use SpringToolSuite to create a <code>Java 8</code> SpringBoot project with below dependencies:

<pre><code class="language-html">
&lt;dependency&gt;
	&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
	&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
	&lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
	&lt;groupId&gt;org.postgresql&lt;/groupId&gt;
	&lt;artifactId&gt;postgresql&lt;/artifactId&gt;
	&lt;scope&gt;runtime&lt;/scope&gt;
&lt;/dependency&gt;
</code></pre>
<h2 id="spring-boot-one-to-one-models">SpringBoot OneToOne Models - Spring Boot One to One Mapping Example</h2>
- <code>Student</code> model ->

<pre>code class="language-java">
package com.ozenero.springrestapi.onetoone.model;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
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.JsonIgnoreProperties;

@Entity
@Table(name = "students")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
    
    @Column(name = "name")
	private String name;
    
    @Column(name = "age")
	private int age;
    
    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "student")
    private Contact contact;
	
	public Student() {}
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
	
	public Long getId() {
		return this.id;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
	
	public void setAge(int age) {
		this.age =age;
	}
	
	public int getAge() {
		return this.age;
	}
}</code></pre>

- <code>Contact</code> model :

<pre><code class="language-java">
package com.ozenero.springrestapi.onetoone.model;

import java.io.Serializable;

import javax.persistence.Column;
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.OneToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "contacts")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Contact implements Serializable{
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "city")
    private String city;
    
    @Column(name = "phone")
    private String phone;
    
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;
    
    public Contact() {
    }
    
    public Contact(String city, String phone) {
    	this.city = city;
    	this.phone = phone;
    }
    
    public void setId(Long id) {
    	this.id = id;
    }
    
    public Long getId() {
    	return this.id;
    }
    
    public void setCity(String city) {
    	this.city = city;
    }
    
    public String getCity() {
    	return this.city;
    }
    
    public void setPhone(String phone) {
    	this.phone = phone;
    }
    
    public String getPhone() {
    	return this.phone;
    }
    
    public void setStudent(Student student) {
    	this.student = student;
    }
    
    public Student getStudent() {
    	return this.student;
    }
}</code></pre>
<h2 id="spring-boot-jpa-repository">SpringBoot JPA Repositories - Spring Boot One to One Mapping Example</h2>
- <code>StudentRepository</code> :

<pre><code class="language-java">
package com.ozenero.springrestapi.onetoone.jpa;

import org.springframework.data.jpa.repository.JpaRepository;

import com.ozenero.springrestapi.onetoone.model.Student;

public interface StudentRepository extends JpaRepository<Student, Long> {
}</code></pre>

- <code>ContactRepository</code> model :

<pre><code class="language-java">
package com.ozenero.springrestapi.onetoone.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.ozenero.springrestapi.onetoone.model.Contact;

public interface ContactRepository extends JpaRepository<Contact, Long> {
	List<Contact> findByStudentId(Long studentId);	
}</code></pre>

Add datasource configurations in <code>application.properties</code> file :

<pre><code class="language-java">
spring.datasource.url=jdbc:postgresql://localhost/testdb
spring.datasource.username=postgres
spring.datasource.password=123
spring.jpa.generate-ddl=true
#spring.jackson.serialization.fail-on-empty-beans=false
</code></pre>
<h2 id="spriing-boot-restapi">Expose SpringBoot Rest APIs - Spring Boot One to One Mapping Example</h2>
- <code>StudentController</code> :

<pre><code class="language-java">
package com.ozenero.springrestapi.onetoone.rest;

import java.util.List;
import java.util.Optional;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
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 com.ozenero.springrestapi.onetoone.exception.NotFoundException;
import com.ozenero.springrestapi.onetoone.jpa.StudentRepository;
import com.ozenero.springrestapi.onetoone.model.Student;

@RestController
@RequestMapping("/api")
public class StudentController {
	
	@Autowired
	private StudentRepository studentRepository;
	
    @GetMapping("/students")
    public List<Student> getAllStudents() {
    	return studentRepository.findAll();
    }
    
    @GetMapping("/students/{id}")
    public Student getStudentByID(@PathVariable Long id) {
    	Optional<Student> optStudent = studentRepository.findById(id);
    	if(optStudent.isPresent()) {
    		return optStudent.get();
    	}else {
    		throw new NotFoundException("Student not found with id " + id);
    	}
    }
    
    @PostMapping("/students")
    public Student createStudent(@Valid @RequestBody Student student) {
        return studentRepository.save(student);
    }
    
    @PutMapping("/students/{id}")
    public Student updateStudent(@PathVariable Long id,
                                   @Valid @RequestBody Student studentUpdated) {
        return studentRepository.findById(id)
                .map(student -> {
                    student.setName(studentUpdated.getName());
                    student.setAge(studentUpdated.getAge());
                    return studentRepository.save(student);
                }).orElseThrow(() -> new NotFoundException("Student not found with id " + id));
    }
    
    @DeleteMapping("/students/{id}")
    public String deleteStudent(@PathVariable Long id) {
        return studentRepository.findById(id)
                .map(student -> {
                    studentRepository.delete(student);
                    return "Delete Successfully!";
                }).orElseThrow(() -> new NotFoundException("Student not found with id " + id));
    }
}</code></pre>

- <code>ContactController</code> :

<pre><code class="language-java">
package com.ozenero.springrestapi.onetoone.rest;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
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 com.ozenero.springrestapi.onetoone.exception.NotFoundException;
import com.ozenero.springrestapi.onetoone.jpa.ContactRepository;
import com.ozenero.springrestapi.onetoone.jpa.StudentRepository;
import com.ozenero.springrestapi.onetoone.model.Contact;

@RestController
@RequestMapping("/api")
public class ContactController {
	@Autowired
	private ContactRepository contactRepository;
	
	@Autowired
	private StudentRepository studentRepository;
	
	@GetMapping("/contacts")
	public List<Contact> getAllContacts(){
		return contactRepository.findAll();
	}
	
    @GetMapping("/students/{studentId}/contacts")
    public Contact getContactByStudentId(@PathVariable Long studentId) {
    	
        if(!studentRepository.existsById(studentId)) {
            throw new NotFoundException("Student not found!");
        }
    	
    	List<Contact> contacts = contactRepository.findByStudentId(studentId);
    	if(contacts.size() > 0) {
    		return contacts.get(0);
    	}else {
    		throw new NotFoundException("Contact not found!");
    	}
    }
    
    @PostMapping("/students/{studentId}/contacts")
    public Contact addContact(@PathVariable Long studentId,
                            @Valid @RequestBody Contact contact) {
        return studentRepository.findById(studentId)
                .map(student -> {
                    contact.setStudent(student);
                    return contactRepository.save(contact);
                }).orElseThrow(() -> new NotFoundException("Student not found!"));
    }
    
    @PutMapping("/contacts/{contactId}")
    public Contact updateContact(@PathVariable Long contactId,
                               @Valid @RequestBody Contact contactUpdated) {
        return contactRepository.findById(contactId)
                .map(contact -> {
                    contact.setCity(contactUpdated.getCity());
                    contact.setPhone(contactUpdated.getPhone());
                    return contactRepository.save(contact);
                }).orElseThrow(() -> new NotFoundException("Contact not found!"));
    }
    
    @DeleteMapping("/contacts/{contactId}")
    public String deleteContact(@PathVariable Long contactId) {
        return contactRepository.findById(contactId)
                .map(contact -> {
                    contactRepository.delete(contact);
                    return "Deleted Successfully!";
                }).orElseThrow(() -> new NotFoundException("Contact not found!"));
    }
}</code></pre>
<h2 id="run-check-result">Run & Check Results</h2>
- Run the SpringBoot project with commandline <code>mvn spring-boot:run</code>.
2 tables is created in PostgreSQL :

[caption id="attachment_4774" align="alignnone" width="358"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-Jpa-One-To-One-Postgresql-Tables-Created-By-JPA.png" alt="Spring Boot Rest Apis Hibernate Spring Jpa One To One Postgresql Tables Created By JPA" width="358" height="310" class="size-full wp-image-4774" /> Spring Boot Rest Apis Hibernate Spring Jpa One To One Postgresql Tables Created By JPA[/caption]

- Add Students :

[caption id="attachment_4775" align="alignnone" width="572"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-to-one-Postgresql-Tables-post-a-student-data.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables - post a student data" width="572" height="463" class="size-full wp-image-4775" /> Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables - post a student data[/caption]

- Add Contacts :

[caption id="attachment_4776" align="alignnone" width="566"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-Jpa-One-to-one-Postgresql-Post-a-Contact.png" alt="Spring Boot Rest Apis Hibernate Spring Jpa One-to-one Postgresql Post a Contact" width="566" height="531" class="size-full wp-image-4776" /> Spring Boot Rest Apis Hibernate Spring Jpa One-to-one Postgresql Post a Contact[/caption]

- Update Contact :

[caption id="attachment_4777" align="alignnone" width="569"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-to-one-Postgresql-Put-Contact.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Put Contact" width="569" height="527" class="size-full wp-image-4777" /> Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Put Contact[/caption]

- Update Student :

[caption id="attachment_4778" align="alignnone" width="588"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-to-one-Postgresql-Put-a-student.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Put a student" width="588" height="464" class="size-full wp-image-4778" /> Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Put a student[/caption]

- Get All Students :

[caption id="attachment_4779" align="alignnone" width="332"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-Jpa-one-to-one-Postgresql-Get-All-Student.png" alt="Spring Boot Rest Apis Hibernate Spring Jpa one-to-one Postgresql Get All Student" width="332" height="370" class="size-full wp-image-4779" /> Spring Boot Rest Apis Hibernate Spring Jpa one-to-one Postgresql Get All Student[/caption]

- Get All Contacts :

[caption id="attachment_4780" align="alignnone" width="330"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-Jpa-One-To-One-Postgresql-Get-All-Contacts.png" alt="Spring Boot Rest Apis Hibernate Spring Jpa One To One Postgresql Get All Contacts" width="330" height="439" class="size-full wp-image-4780" /> Spring Boot Rest Apis Hibernate Spring Jpa One To One Postgresql Get All Contacts[/caption]

- Delete a Student :

[caption id="attachment_4781" align="alignnone" width="426"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-to-one-Postgresql-Tables-Delete-a-Student.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables Delete a Student" width="426" height="387" class="size-full wp-image-4781" /> Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables Delete a Student[/caption]

- Delete a Contact :

[caption id="attachment_4782" align="alignnone" width="392"]<img src="https://loizenai.com/wp-content/uploads/2021/01/Spring-Boot-Rest-Apis-Hibernate-Spring-JPA-One-to-one-Postgresql-Tables-Delete-a-Contact.png" alt="Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables Delete a Contact" width="392" height="385" class="size-full wp-image-4782" /> Spring Boot Rest Apis Hibernate Spring JPA One-to-one Postgresql Tables Delete a Contact[/caption]

<strong>Note</strong>: PostgreSQL commandline :

<code>PostgreSQL\9.6\bin>psql.exe --username="postgres" -W</code>: connect to PostgreSQL ->
<pre><code class="language-java">
C:\Program Files\PostgreSQL\9.6\bin>psql.exe --username="postgres" -W
Password for user postgres:
psql (9.6.9)
WARNING: Console code page (437) differs from Windows code page (1252)
         8-bit characters might not work correctly. See psql reference
         page "Notes for Windows users" for details.
Type "help" for help.
</code></pre>

- <code>\l</code>:  List of databases

- <code>\c testdb</code>: connect to "testdb"

- <code>\d</code>: List of relations ->
<pre>
testdb=# \d
               List of relations
 Schema |      Name       |   Type   |  Owner
--------+-----------------+----------+----------
 public | contacts        | table    | postgres
 public | contacts_id_seq | sequence | postgres
 public | students        | table    | postgres
 public | students_id_seq | sequence | postgres
(4 rows)
</pre>

- <code>testdb=# \q</code>: Exit 
<h2 id="read-more">Read More</h2>
<div class="div-loizenai-pre-related-post">
Related posts:
<hr>
<ul class="ul-loizenai-pre-related-post">
<li><a href="https://loizenai.com/node-js-upload-image-to-postgresql-example/">Node.js Upload Image to PostgreSQL Example</a></li>
<li><a href="https://loizenai.com/image-to-base64-java-8/">Image to Base64 with Java 8</a></li>
<li><a href="https://loizenai.com/springboot-angular-10-postgresql-crud-restapi-example/">SpringBoot Angular 10 PostgreSQL CRUD RestAPI Example</a></li>
<li><a href="https://loizenai.com/angular-8-springboot-postgresql-crud-example/">Angular 8 SpringBoot PostgreSQL CRUD Example</a></li>
<li><a href="https://loizenai.com/springboot-angular-9-postgresql-crud-example-architecture-diagram/">SpringBoot Angular 9 PostgreSQL CRUD Example</a></li>
<li><a href="https://loizenai.com/sequelize-one-to-one-example/">Sequelize One to One Example</a></li>
<li><a href="https://loizenai.com/node-js-postgresql-crud-example/">Node.js PostgreSQL CRUD Example</a></li>
</ul>
</div>

<h2 id="sourcecode">SourceCode</h2>
<p class="sourcecode-lzai">
<a href="https://loizenai.com/wp-content/uploads/2021/01/SpringBoot-RestAPIs-OneToOne.zip">SpringBoot-RestAPIs-OneToOne</a>
</p>