Spring Boot Hibernate One-to-One CRUD REST API Example

In this tutorial, we will demonstrate how to set up a one-to-one relationship between User and Address entities using Spring Boot and Hibernate, and expose CRUD operations through a REST API.

Prerequisites

  1. Java Development Kit (JDK) 11 or higher: Ensure JDK is installed and configured on your system.
  2. Integrated Development Environment (IDE): IntelliJ IDEA, Eclipse, or any other IDE.
  3. Maven: Ensure Maven is installed and configured on your system.

Step 1: Create a Spring Boot Project

  1. Open your IDE and create a new Spring Boot project.
  2. Use Spring Initializr or manually create the pom.xml file to include Spring Boot and other required dependencies.
<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>com.example</groupId>
    <artifactId>spring-boot-one-to-one-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/>
    </parent>

    <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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Explanation

  • spring-boot-starter-data-jpa: Includes Spring Data JPA with Hibernate.
  • spring-boot-starter-web: Includes Spring MVC for building web applications.
  • h2: An in-memory database for testing purposes.

Step 2: Configure the Application Properties

Configure the application.properties file to set up the H2 database.

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Step 3: Create the User Entity Class

Create a package named com.example.entity and a class named User.

package com.example.entity;

import jakarta.persistence.*;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    private Address address;

    public User() {}

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        if (address == null) {
            if (this.address != null) {
                this.address.setUser(null);
            }
        } else {
            address.setUser(this);
        }
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{id=" + id + ", username='" + username + '\'' + ", email='" + email + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.
  • @OneToOne: Defines a one-to-one relationship with the Address entity.
  • mappedBy: Specifies the field in the Address entity that owns the relationship.
  • cascade: Specifies the cascade operations.
  • fetch: Specifies the fetch type (lazy loading).
  • optional: Indicates whether the relationship is optional.

Step 4: Create the Address Entity Class

Create a class named Address in the same package.

package com.example.entity;

import jakarta.persistence.*;

@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String street;
    private String city;
    private String state;
    private String zipCode;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    public Address() {}

    public Address(String street, String city, String state, String zipCode) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
    }

    public Long getId() {
        return id;
    }

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

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Address{id=" + id + ", street='" + street + '\'' + ", city='" + city + '\'' + ", state='" + state + '\'' + ", zipCode='" + zipCode + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.
  • @OneToOne: Defines a one-to-one relationship with the User entity.
  • @JoinColumn: Specifies the foreign key column.

Step 5: Create Repository Interfaces

Create a package named com.example.repository and interfaces for User and Address.

package com.example.repository;

import com.example.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
package com.example.repository;

import com.example.entity.Address;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AddressRepository extends JpaRepository<Address, Long> {}

Step 6: Create Service Classes

Create a package named com.example.service and service classes for User and Address.

package com.example.service;

import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    
    private UserRepository userRepository;
    
    public UserService(UserRepository userRepository){
    	this.userRepository = userRepository;
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
}
package com.example.service;

import com.example.entity.Address;
import com.example.repository.AddressRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AddressService {

    private AddressRepository addressRepository;
    
    public AddressService(AddressRepository addressRepository){
    	this.addressRepository = addressRepository;
    }

    public Address save(Address address) {
        return addressRepository.save(address);
    }

    public List<Address> findAll() {
        return addressRepository.findAll();
    }

    public Address findById(Long id) {
        return addressRepository.findById(id).orElse(null);
    }



    public void deleteById(Long id) {
        addressRepository.deleteById(id);
    }
}

Step 7: Create Controller Classes

Create a package named com.example.controller and controller classes for User and Address.

package com.example.controller;

import com.example.entity.User;
import com.example.entity.Address;
import com.example.service.UserService;
import com.example.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    private UserService userService;

    private AddressService addressService;
    
    public UserController(UserService userService, AddressService addressService){
    	this.userService = userService;
        this.addressService = addressService;
    }


    @PostMapping
    public User createUser(@RequestBody User user) {
        if (user.getAddress() != null) {
            user.getAddress().setUser(user);
        }
        return userService.save(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.findById(id);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
    }

    @PostMapping("/{userId}/address")
    public User addAddress(@PathVariable Long userId, @RequestBody Address address) {
        User user = userService.findById(userId);
        if (user != null) {
            address.setUser(user);
            user.setAddress(address);
            userService.save(user);
            return user;
        }
        return null;
    }

    @DeleteMapping("/{userId}/address")
    public User removeAddress(@PathVariable Long userId) {
        User user = userService.findById(userId);
        if (user != null && user.getAddress() != null) {
            addressService.deleteById(user.getAddress().getId());
            user.setAddress(null);
            return userService.save(user);
        }
        return null;
    }
}
package com.example.controller;

import com.example.entity.Address;
import com.example.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/addresses")
public class AddressController {

    private AddressService addressService;
    
    public AddressController(AddressService addressService){
    	this.addressService = addressService;
    }

    @PostMapping
    public Address createAddress(@RequestBody Address address) {
        return addressService.save(address);
    }

    @GetMapping
    public List<Address> getAllAddresses() {
        return addressService.findAll();
    }

    @GetMapping("/{id}")
    public Address getAddressById(@PathVariable Long id) {
        return addressService.findById(id);
    }

    @DeleteMapping("/{id}")
    public void deleteAddress(@PathVariable Long id) {
        addressService.deleteById(id);
    }
}

Step 8: Create Main Application Class

Create a package named com.example and a class named SpringBootOneToOneExampleApplication.

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Step 9: Run the Application

  1. Run the SpringBootOneToOneExampleApplication class.
  2. Use an API client (e.g., Postman) or a web browser to test the endpoints.

Testing the Endpoints

  1. Create a User:

    • URL: POST /users
    • Body:
      {
        "username": "john_doe",
        "email": "john@example.com",
        "address": {
          "street": "123 Main St",
          "city": "Springfield",
          "state": "IL",
          "zipCode": "62701"
        }
      }
      
  2. Create an Address:

    • URL: POST /addresses
    • Body:
      {
        "street": "123 Main St",
        "city": "Springfield",
        "state": "IL",
        "zipCode": "62701"
      }
      
  3. Get All Users:

    • URL: GET /users
  4. Get User by ID:

    • URL: GET /users/{id}
  5. Delete User by ID:

    • URL: DELETE /users/{id}
  6. Add Address to User:

    • URL: POST /users/{userId}/address
    • Body:
      {
        "street": "123 Main St",
        "city": "Springfield",
        "state": "IL",
        "zipCode": "62701"
      }
      
  7. Remove Address from User:

    • URL: DELETE /users/{userId}/address
  8. Get All Addresses:

    • URL: GET /addresses
  9. Get Address by ID:

    • URL: GET /addresses/{id}
  10. Delete Address by ID:

  • URL: DELETE /addresses/{id}

Conclusion

You have successfully created an example using Spring Boot and Hibernate to demonstrate a one-to-one relationship between User and Address entities. This tutorial covered setting up a Spring Boot project, configuring Hibernate, creating entity classes with a one-to-one relationship, and performing CRUD operations through RESTful endpoints.


Comments