One to one

Consider two entities: Employee and EmployeeDetails. We want to establish a one-to-one relationship between them, such that each employee has exactly one set of employee details. We'll use the @OneToOne annotation to define this relationship.

First, let's define the Employee entity:

            
  @Entity
  @Table(name = "employees")
  public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToOne(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    private EmployeeDetails employeeDetails;

    // constructors, getters, and setters
  }
            
      

Here, we annotate the Employee class with @Entity and @Table to mark it as an entity and specify the database table name. We define the primary key using @Id and @GeneratedValue annotations.

We then define the one-to-one relationship with EmployeeDetails using the @OneToOne annotation. We specify that the relationship is mapped by the employee field in the EmployeeDetails class using the mappedBy attribute. We also specify that the relationship is optional (optional = false), meaning that each Employee must have exactly one set of EmployeeDetails.

We specify that the relationship should cascade all changes (cascade = CascadeType.ALL), meaning that any changes made to an Employee should be propagated to the associated EmployeeDetails. We also specify that the relationship should be fetched lazily (fetch = FetchType.LAZY), meaning that the associated EmployeeDetails should only be loaded when explicitly requested.

Now let's define the EmployeeDetails entity:
            
    @Entity
    @Table(name = "employee_details")
    public class EmployeeDetails {
        @Id
        private Long id;
        private String address;
        private String phone;

        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "employee_id")
        private Employee employee;

        // constructors, getters, and setters
    }
            
      

Again, we annotate the EmployeeDetails class with @Entity and @Table to mark it as an entity and specify the database table name. We define the primary key using @Id.

We then define the one-to-one relationship with Employee using the @OneToOne annotation. We specify that the relationship should be fetched lazily (fetch = FetchType.LAZY). We also specify a join column using the @JoinColumn annotation, specifying that the employee_id column should be used to join the Employee and EmployeeDetails tables.

With these two entities defined and the one-to-one relationship established, we can use Hibernate to perform CRUD operations on the entities and persist them to the database.

Here's an example of how to use Hibernate to save an Employee and its associated EmployeeDetails to the database:
            
      Employee employee = new Employee();
      employee.setName("John Doe");

      EmployeeDetails employeeDetails = new EmployeeDetails();
      employeeDetails.setAddress("123 Main St");
      employeeDetails.setPhone("555-1234");
      employeeDetails.setEmployee(employee);
      employee.setEmployeeDetails(employeeDetails);

      Session session = sessionFactory.openSession();
      Transaction tx = session.beginTransaction();

      session.save(employee);

      tx.commit();
      session.close();
            
      

In this example, we create a new Employee object and set its name. We also create a new EmployeeDetails object, set its address, phone, and associated Employee, and set the Employee's EmployeeDetails field to the new EmployeeDetails object. We then open a Hibernate session, begin a transaction, save the Employee object (which will also save the associated EmployeeDetails object), commit the transaction, and close the session.

The example I provided is a bidirectional one-to-one mapping, where both the Employee and EmployeeDetails entities have a reference to each other through the employeeDetails and employee fields, respectively. This allows you to navigate the relationship from both sides.

However, if you remove the employee field from the EmployeeDetails entity, the mapping becomes unidirectional, where you can only navigate the relationship from the Employee side. In this case, the @OneToOne annotation would be used on the employeeDetails field in the Employee entity, and the @JoinColumn annotation would be used on the employee_id field in the EmployeeDetails entity to specify the foreign key column:

            
  @Entity
  @Table(name = "employees")
  public class Employee {

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

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "employee_details_id")
    private EmployeeDetails employeeDetails;

    // constructors, getters, and setters
  }
            
      
            
  @Entity
  @Table(name = "employee_details")
  public class EmployeeDetails {

      @Id
      private Long id;
      private String address;
      private String phone;

      // constructors, getters, and setters
  }
            
      

In this example, the @OneToOne annotation is used on the employeeDetails field in the Employee entity, specifying that the relationship is optional (optional = false), meaning that each Employee must have exactly one set of EmployeeDetails. The @JoinColumn annotation is used on the employee_details_id field in the Employee entity to specify the foreign key column.

On the EmployeeDetails side, the mapping is no longer bidirectional, so the employee field is removed.

I hope this clarifies the difference between unidirectional and bidirectional one-to-one mappings in Hibernate!