Learnitweb

One-to-One Mapping in Hibernate

Introduction

One-to-One mapping in Hibernate represents a relationship where each record in one table corresponds to exactly one record in another table. This is commonly used when entities have exclusive relationships, such as User and Address.

In this tutorial, we will:

  • Create a User entity with a one-to-one relationship to an Address entity.
  • Generate database tables in Oracle Express Edition.
  • Perform CRUD operations.

Project Setup

Ensure you have the following dependencies in your pom.xml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<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.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <scope>runtime</scope> </dependency>
<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.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <scope>runtime</scope>
</dependency>

Database Configuration

Configure the application.properties file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=admin
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
# JPA Properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe spring.datasource.username=system spring.datasource.password=admin spring.datasource.driver-class-name=oracle.jdbc.OracleDriver # JPA Properties spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=admin
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver

# JPA Properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect

Creating entities

User Entity

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.hibernatedemo.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
address.setUser(this);
}
}
package com.learnitweb.hibernatedemo.entity; import jakarta.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; address.setUser(this); } }
package com.learnitweb.hibernatedemo.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
        address.setUser(this);
    }
}

Address Entity

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.hibernatedemo.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
private String country;
@OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
private User user;
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 getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
package com.learnitweb.hibernatedemo.entity; import jakarta.persistence.*; @Entity @Table(name = "address") public class Address { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String street; private String city; private String country; @OneToOne(mappedBy = "address", cascade = CascadeType.ALL) private User user; 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 getCountry() { return country; } public void setCountry(String country) { this.country = country; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
package com.learnitweb.hibernatedemo.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String street;
    private String city;
    private String country;

    @OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
    private User user;

    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 getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public User getUser() {
        return user;
    }

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

SQL Statements to Create Tables

For Oracle Express Edition, use the following SQL statements:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
CREATE TABLE address (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
street VARCHAR2(255),
city VARCHAR2(255),
country VARCHAR2(255)
);
CREATE TABLE users (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR2(255),
address_id NUMBER UNIQUE,
CONSTRAINT fk_address FOREIGN KEY (address_id) REFERENCES address(id)
);
CREATE TABLE address ( id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, street VARCHAR2(255), city VARCHAR2(255), country VARCHAR2(255) ); CREATE TABLE users ( id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name VARCHAR2(255), address_id NUMBER UNIQUE, CONSTRAINT fk_address FOREIGN KEY (address_id) REFERENCES address(id) );
CREATE TABLE address (
    id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    street VARCHAR2(255),
    city VARCHAR2(255),
    country VARCHAR2(255)
);

CREATE TABLE users (
    id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name VARCHAR2(255),
    address_id NUMBER UNIQUE,
    CONSTRAINT fk_address FOREIGN KEY (address_id) REFERENCES address(id)
);

Creating Repositories

User Repository

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {}
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> {}
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {}

Address Repository

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import org.springframework.data.jpa.repository.JpaRepository;
public interface AddressRepository extends JpaRepository<Address, Long> {}
import org.springframework.data.jpa.repository.JpaRepository; public interface AddressRepository extends JpaRepository<Address, Long> {}
import org.springframework.data.jpa.repository.JpaRepository;
public interface AddressRepository extends JpaRepository<Address, Long> {}

Service Layer

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.hibernatedemo.service;
import com.learnitweb.hibernatedemo.entity.Address;
import com.learnitweb.hibernatedemo.entity.User;
import com.learnitweb.hibernatedemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
public User updateUser(Long id, User userDetails) {
return userRepository.findById(id).map(user -> {
user.setName(userDetails.getName());
user.setAddress(userDetails.getAddress());
return userRepository.save(user);
}).orElseThrow(() -> new RuntimeException("User not found"));
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
public User patchUser(Long id, User userDetails) {
return userRepository.findById(id).map(user -> {
user.setName(userDetails.getName());
Address existingAddress = user.getAddress();
existingAddress.setCountry(userDetails.getAddress().getCountry());
existingAddress.setCity(userDetails.getAddress().getCity());
existingAddress.setStreet(userDetails.getAddress().getCity());
user.setAddress(existingAddress);
return userRepository.save(user);
}).orElseThrow(() -> new RuntimeException("User not found"));
}
}
package com.learnitweb.hibernatedemo.service; import com.learnitweb.hibernatedemo.entity.Address; import com.learnitweb.hibernatedemo.entity.User; import com.learnitweb.hibernatedemo.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Optional; @Service public class UserService { @Autowired private UserRepository userRepository; public User createUser(User user) { return userRepository.save(user); } public Optional<User> getUserById(Long id) { return userRepository.findById(id); } public User updateUser(Long id, User userDetails) { return userRepository.findById(id).map(user -> { user.setName(userDetails.getName()); user.setAddress(userDetails.getAddress()); return userRepository.save(user); }).orElseThrow(() -> new RuntimeException("User not found")); } public void deleteUser(Long id) { userRepository.deleteById(id); } public User patchUser(Long id, User userDetails) { return userRepository.findById(id).map(user -> { user.setName(userDetails.getName()); Address existingAddress = user.getAddress(); existingAddress.setCountry(userDetails.getAddress().getCountry()); existingAddress.setCity(userDetails.getAddress().getCity()); existingAddress.setStreet(userDetails.getAddress().getCity()); user.setAddress(existingAddress); return userRepository.save(user); }).orElseThrow(() -> new RuntimeException("User not found")); } }
package com.learnitweb.hibernatedemo.service;

import com.learnitweb.hibernatedemo.entity.Address;
import com.learnitweb.hibernatedemo.entity.User;
import com.learnitweb.hibernatedemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

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

    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }

    public User updateUser(Long id, User userDetails) {
        return userRepository.findById(id).map(user -> {
            user.setName(userDetails.getName());
            user.setAddress(userDetails.getAddress());
            return userRepository.save(user);
        }).orElseThrow(() -> new RuntimeException("User not found"));
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }

    public User patchUser(Long id, User userDetails) {
        return userRepository.findById(id).map(user -> {
            user.setName(userDetails.getName());
            Address existingAddress = user.getAddress();
            existingAddress.setCountry(userDetails.getAddress().getCountry());
            existingAddress.setCity(userDetails.getAddress().getCity());
            existingAddress.setStreet(userDetails.getAddress().getCity());

            user.setAddress(existingAddress);

            return userRepository.save(user);
        }).orElseThrow(() -> new RuntimeException("User not found"));
    }
}

Controller

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.hibernatedemo.controller;
import com.learnitweb.hibernatedemo.entity.User;
import com.learnitweb.hibernatedemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
return ResponseEntity.ok(userService.createUser(user));
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
Optional<User> user = userService.getUserById(id);
return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
}
@PatchMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return ResponseEntity.ok(userService.patchUser(id, userDetails));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
package com.learnitweb.hibernatedemo.controller; import com.learnitweb.hibernatedemo.entity.User; import com.learnitweb.hibernatedemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Optional; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public ResponseEntity<User> createUser(@RequestBody User user) { return ResponseEntity.ok(userService.createUser(user)); } @GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { Optional<User> user = userService.getUserById(id); return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } @PatchMapping("/{id}") public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) { return ResponseEntity.ok(userService.patchUser(id, userDetails)); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { userService.deleteUser(id); return ResponseEntity.noContent().build(); } }
package com.learnitweb.hibernatedemo.controller;

import com.learnitweb.hibernatedemo.entity.User;
import com.learnitweb.hibernatedemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

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

    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        return ResponseEntity.ok(userService.createUser(user));
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        Optional<User> user = userService.getUserById(id);
        return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }

    @PatchMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
        return ResponseEntity.ok(userService.patchUser(id, userDetails));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }
}

Explanation of Annotations

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
@OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address;
@OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
  • Specifies the foreign key column in the owning entity.
  • name: Defines the foreign key column in the current table.
  • referencedColumnName: Defines which column in the referenced table the foreign key points to.
  • In User, @JoinColumn(name = "address_id", referencedColumnName = "id") means that address_id in the users table references id in the address table.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
private User user;
@OneToOne(mappedBy = "address", cascade = CascadeType.ALL) private User user;
    @OneToOne(mappedBy = "address", cascade = CascadeType.ALL)
    private User user;
  • Used in the non-owning entity to establish the bidirectional relationship.
  • mappedBy: Specifies the field in the owning entity that maps this relationship.
  • cascade = CascadeType.ALL: Ensures that any operation (persist, merge, remove) cascades to the related entity.
  • In Address, @OneToOne(mappedBy = "address", cascade = CascadeType.ALL) links it back to the User entity, indicating that User owns the relationship.