Hibernate 5 - One to One association example

Posted on April 1, 2017


Technologies used:   JDK 1.8.0_121 | Hibernate 5.2.6.Final | Maven 3.3.9 | MySQL 5.7.12 | Eclipse Neon.3

In one-to-one association, source entity has a field that references another target entity. The @OneToOne JPA annotation is used to map the source entity with the target entity.

The one-to-one association can be either unidirectional or bidirectional.  

In unidirectional association, source entity has a relationship field that refers to the target entity and the source entity’s table contains the foreign key.

In 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.

Unidirectional one-to-one association example

Consider the following domain model and relational model diagrams of one-to-one unidirectional association.

one-to-one-uni-domain-model.png

one-to-one-uni.png

Jar dependencies

Add the following jar dependencies for Hibernate and MySQL driver in pom.xml file.

<dependencies>
  <!-- Mysql Connector -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.5</version>
  </dependency>
  <!-- Hibernate 5.2.6 Final -->
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.2.6.Final</version>
  </dependency>
</dependencies>

Entity class

Create two @Entity classes - User and UserDetail, to map with USERS and USER_DETAILS tables respectively.

User.java

package com.boraji.tutorial.hibernate.entity;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

/**
 * @author imssbora
 */
@Entity
@Table(name = "USERS")
public class User {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "USR_ID")
   private long id;

   @Column(name = "USERNAME", nullable = false, unique = true)
   private String username;

   @Column(name = "PASSWORD")
   private String password;

   @OneToOne(cascade = CascadeType.ALL)
   @JoinColumn(name="USR_DET_ID")
   private UserDetail userDetail;

   //Setter and Getter methods
}

UserDetail.java

package com.boraji.tutorial.hibernate.entity;

import java.time.LocalDate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author imssbora
 */
@Entity
@Table(name = "USER_DETAILS")
public class UserDetail {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "USR_DET_ID")
   private long id;

   @Column(name = "FIRST_NAME")
   private String firstName;

   @Column(name = "LAST_NAME")
   private String lastName;

   @Column(name = "EMAIL")
   private String email;

   @Column(name = "DBO")
   private LocalDate dob;
   
   //Setter and Getter methods
}

Hibernate utility class

Create a helper class HibernateUtil to bootstrap hibernate.

Map the User and UserDetail entities using the #MetadataSources.addAnnotatedClass() method.

HibernateUtil.java

package com.boraji.tutorial.hibernate;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import com.boraji.tutorial.hibernate.entity.User;
import com.boraji.tutorial.hibernate.entity.UserDetail;

public class HibernateUtil {
   private static StandardServiceRegistry registry;
   private static SessionFactory sessionFactory;

   public static SessionFactory getSessionFactory() {
      if (sessionFactory == null) {
         try {
            StandardServiceRegistryBuilder registryBuilder = 
                new StandardServiceRegistryBuilder();

            Map<String, String> settings = new HashMap<>();
            settings.put("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver");
            settings.put("hibernate.connection.url", "jdbc:mysql://localhost:3306/BORAJI");
            settings.put("hibernate.connection.username", "root");
            settings.put("hibernate.connection.password", "admin");
            settings.put("hibernate.show_sql", "true");
            settings.put("hibernate.hbm2ddl.auto", "update");

            registry = registryBuilder.applySettings(settings).build();

            MetadataSources sources = new MetadataSources(registry)
                  .addAnnotatedClass(User.class)
                  .addAnnotatedClass(UserDetail.class);

            Metadata metadata = sources.getMetadataBuilder().build();

            sessionFactory = metadata.getSessionFactoryBuilder().build();
         } catch (Exception e) {
            System.out.println("SessionFactory creation failed");
            if (registry != null) {
               StandardServiceRegistryBuilder.destroy(registry);
            }
         }
      }
      return sessionFactory;
   }

   public static void shutdown() {
      if (registry != null) {
         StandardServiceRegistryBuilder.destroy(registry);
      }
   }
}

 

Main class 

Create the MainApp class to run the application.

MainApp.java

package com.boraji.tutorial.hibernate;

import java.time.LocalDate;
import java.time.Month;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.boraji.tutorial.hibernate.entity.User;
import com.boraji.tutorial.hibernate.entity.UserDetail;

/**
 * @author imssbora
 */
public class MainApp {
   public static void main(String[] args) {
      Session session = null;
      Transaction transaction = null;
      try {
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();

         User user = new User();
         user.setUsername("USR001");
         user.setPassword("[email protected]");

         UserDetail userDetail = new UserDetail();
         userDetail.setFirstName("Michael");
         userDetail.setLastName("Smith");
         userDetail.setEmail("[email protected]");
         userDetail.setDob(LocalDate.of(1985, Month.APRIL, 1));

         user.setUserDetail(userDetail);

         session.persist(user);
         transaction.commit();

         System.out.println("User saved successfully");

      } catch (Exception e) {
         if (transaction != null) {
            System.out.println("Transaction is being rolled back.");
            transaction.rollback();
         }
         e.printStackTrace();
      } finally {
         if (session != null) {
            session.close();
         }
      }
      HibernateUtil.shutdown();
   }
}

Output

Hibernate: create table USER_DETAILS (USR_DET_ID bigint not null auto_increment, DBO date, EMAIL varchar(255), FIRST_NAME varchar(255), LAST_NAME varchar(255), primary key (USR_DET_ID))
Hibernate: create table USERS (USR_ID bigint not null auto_increment, PASSWORD varchar(255), USERNAME varchar(255) not null, USR_DET_ID bigint, primary key (USR_ID))
Hibernate: alter table USERS drop constraint UK_h6k33r31i2nvrri9lok4r163j
Hibernate: alter table USERS add constraint UK_h6k33r31i2nvrri9lok4r163j unique (USERNAME)
Hibernate: alter table USERS add constraint FK7ka8bhnkcddmpykt2njua5a6e foreign key (USR_DET_ID) references USER_DETAILS (USR_DET_ID)
Hibernate: insert into USER_DETAILS (DBO, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)
Hibernate: insert into USERS (PASSWORD, USR_DET_ID, USERNAME) values (?, ?, ?)
User saved successfully

After executing the MainApp class, you will see the following records in USERS, and USER_DETAILS tables of MySQL database.

mysql> select * from users;
+--------+----------+----------+------------+
| USR_ID | PASSWORD | USERNAME | USR_DET_ID |
+--------+----------+----------+------------+
|      1 | [email protected] | USR001   |          1 |
+--------+----------+----------+------------+
1 row in set (0.00 sec)

mysql> select * from user_details;
+------------+------------+---------------------------+------------+-----------+
| USR_DET_ID | DBO        | EMAIL                     | FIRST_NAME | LAST_NAME |
+------------+------------+---------------------------+------------+-----------+
|          1 | 1985-04-01 | [email protected] | Michael    | Smith     |
+------------+------------+---------------------------+------------+-----------+
1 row in set (0.00 sec)

 

Bidirectional one-to-one association example

Following are the domain model and relational model diagrams of one-to-one bidirectional association.

one-to-one-bi-domain-model.png

one-to-one-bi.png

Entity class

To define a one-to-one bidirectional association, the @OneToOne annotation must be used in the target entity (i.e.  UserDetail entity) and the mappedBy attribute must be used in the source entity (i.e.  User entity).

Modify the code of User and UserDetail  classes as follows.

User.java

package com.boraji.tutorial.hibernate.entity;

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;

/**
 * @author imssbora
 */
@Entity
@Table(name = "USERS")
public class User {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "USR_ID")
   private long id;

   @Column(name = "USERNAME", nullable = false, unique = true)
   private String username;

   @Column(name = "PASSWORD")
   private String password;

   @OneToOne(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.LAZY)
   private UserDetail userDetail;

   //Setter and getter methods
}

UserDetail.java

package com.boraji.tutorial.hibernate.entity;

import java.time.LocalDate;

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;

/**
 * @author imssbora
 */
@Entity
@Table(name = "USER_DETAILS")
public class UserDetail {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "USR_DET_ID")
   private long id;

   @Column(name = "FIRST_NAME")
   private String firstName;

   @Column(name = "LAST_NAME")
   private String lastName;

   @Column(name = "EMAIL")
   private String email;

   @Column(name = "DBO")
   private LocalDate dob;
   
   @OneToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "USR_ID")
   private User user;

   //Setter and Getter methods
}

 

Hibernate utility class

No changes are needed in the HibernateUtil class. We can use the same class as used in the one-to-one unidirectional association example.

Main class 

Modify the code of the  MainApp class and run the application.

MainApp.java

package com.boraji.tutorial.hibernate;

import java.time.LocalDate;
import java.time.Month;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.boraji.tutorial.hibernate.entity.User;
import com.boraji.tutorial.hibernate.entity.UserDetail;

/**
 * @author imssbora
 */
public class MainApp {
   public static void main(String[] args) {
      Session session = null;
      Transaction transaction = null;
      try {
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();

         User user = new User();
         user.setUsername("USR002");
         user.setPassword("[email protected]");

         UserDetail userDetail = new UserDetail();
         userDetail.setFirstName("Sammer");
         userDetail.setLastName("Dua");
         userDetail.setEmail("[email protected]");
         userDetail.setDob(LocalDate.of(1985, Month.APRIL, 1));
         userDetail.setUser(user);

         user.setUserDetail(userDetail);

         session.persist(user);
         transaction.commit();

         System.out.println("User saved successfully");

      } catch (Exception e) {
         if (transaction != null) {
            System.out.println("Transaction is being rolled back.");
            transaction.rollback();
         }
         e.printStackTrace();
      } finally {
         if (session != null) {
            session.close();
         }
      }
      HibernateUtil.shutdown();
   }
}

Output

Hibernate: create table USER_DETAILS (USR_DET_ID bigint not null auto_increment, DBO date, EMAIL varchar(255), FIRST_NAME varchar(255), LAST_NAME varchar(255), USR_ID bigint, primary key (USR_DET_ID))
Hibernate: create table USERS (USR_ID bigint not null auto_increment, PASSWORD varchar(255), USERNAME varchar(255) not null, primary key (USR_ID))
Hibernate: alter table USERS drop constraint UK_h6k33r31i2nvrri9lok4r163j
Hibernate: alter table USERS add constraint UK_h6k33r31i2nvrri9lok4r163j unique (USERNAME)
Hibernate: alter table USER_DETAILS add constraint FK8tf6iyf643x400h9k51ta9e3a foreign key (USR_ID) references USERS (USR_ID)
Hibernate: insert into USERS (PASSWORD, USERNAME) values (?, ?)
Hibernate: insert into USER_DETAILS (DBO, EMAIL, FIRST_NAME, LAST_NAME, USR_ID) values (?, ?, ?, ?, ?)
User saved successfully

On executing the MainApp class, you will see the following records in USERS, and USER_DETAILS tables of MySQL database.

mysql> select * from users;
+--------+-------------+----------+
| USR_ID | PASSWORD    | USERNAME |
+--------+-------------+----------+
|      1 | [email protected] | USR002   |
+--------+-------------+----------+
1 row in set (0.00 sec)

mysql> select * from user_details;
+------------+------------+------------------------+------------+-----------+--------+
| USR_DET_ID | DBO        | EMAIL                  | FIRST_NAME | LAST_NAME | USR_ID |
+------------+------------+------------------------+------------+-----------+--------+
|          1 | 1985-04-01 | [email protected] | Sammer     | Dua       |      1 |
+------------+------------+------------------------+------------+-----------+--------+
1 row in set (0.00 sec)
Download Sources