In Hibernate or any ORM framework, a java class is mapped to a database table.
This means the fields of the java class correspond to the columns of a database table.

But what if two java classes need to be mapped to a single database table.
Scenario
Consider an entity Employee which represents a typical employee in an organization.

Information about an employee can be split into 2 parts :
1. Basic information such as his employee id and name.
2. Advanced information which provides more details about the employee such as his address, blood group, country etc.

Many times only basic attributes of an employee are required across the organization system.
Thus, it makes sense to put this information about the employee in one class so that the class(and its objects) are light weight.
This makes it easy to transfer the objects of this class across the entire system
.

Keeping in mind the above scenario, an employee can be represented using 2 classes :

Employee
: Contains his employee id and name
EmployeeDetail : Has other fields such as his address, blood group, country etc.

Also, while saving the employee in the database, there is no need to have 2 separate tables which means both the classes should be mapped to one table.
But, How is that possible since one class is mapped to one database table. It is !!!

Mapping 2 classes to 1 Table

As per the above scenario, we have 2 classes Employee and EmployeeDetail.
Employee class is the main entity and is associated with a database table.
EmployeeDetail is associated with Employee but does not have a corresponding database table.

Employee and EmployeeDetail classes share a Has A relationship meaning Employee class has a field of type EmployeeDetail.

Now, how should these be arranged so that when they are saved, the record is inserted into only 1 table.

Hibernate provides two annotations which serve this purpose.

These annotations are:
@Embeddable
Applied over the class which is not directly mapped with a database table but rather it is associated with another class, which, in turn is the actual entity class.
It is used as an instance variable inside the main entity class.
According to the description, this annotation should be applied over EmployeeDetail class.

From official documentation of this annotation

Each of the persistent properties or fields of the embedded object is mapped to the database table for the entity.


@Embedded
Applied over the instance variable of the Embeddable class. This instance variable is placed in the actual entity class.In our case, it will be applied over the instance variable(field) of type EmployeeDetail in Employee class.

From official documentation of this annotation

Specifies a persistent field or property of an entity whose value is an instance of an embeddable class.

After applying the annotations, the classes will look as below.

Employee.java

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * Main entity class which is mapped to the database table
 */
@Entity
public class Employee {

  @Id
  @GeneratedValue
  private int id;

  private String name;

  private String employeeId;

  /**
   * Sub entity which is mapped to the same database table
   */
  @Embedded
  private EmployeeDetail empDetails;
  
  public int getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

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

  public String getEmployeeId() {
    return employeeId;
  }

  public void setEmployeeId(String employeeId) {
    this.employeeId = employeeId;
  }

  public EmployeeDetail getEmpDetails() {
    return empDetails;
  }

  public void setEmpDetails(EmployeeDetail empDetails) {
    this.empDetails = empDetails;
  }
}

EmployeeDetail.java

import javax.persistence.Embeddable;

/**
 * Class which is mapped to the database table of its owing entity class
 */
@Embeddable
public class EmployeeDetail {

  private String bloodGroup;
  
  private int age;
  
  private String country;

  public String getBloodGroup() {
    return bloodGroup;
  }

  public void setBloodGroup(String bloodGroup) {
    this.bloodGroup = bloodGroup;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getCountry() {
    return country;
  }

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

In order to insert an employee record, we need to create objects of both these classes and save them using Hibernate classes. The code is given below.

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class Main {
   public static void main(String[] args) {
     //create configuration object
     Configuration configuration = new Configuration().configure();
     //add class so that it is recognized as entity by hibernate
     configuration.addAnnotatedClass(Employee.class);
     StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
      .applySettings(configuration.getProperties()).build();
     //create a session factory
     SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
     Session session = sessionFactory.openSession();
     //first we need to create EmployeeDetail object
     EmployeeDetail detail = new EmployeeDetail();
     //set details
     detail.setAge(25);
     detail.setCountry("India");
     detail.setBloodGroup("B+");
     //create Employee object
     Employee employee = new Employee();
     //set employee info
     employee.setName("A B");
     employee.setEmployeeId("EMP001");
     //set detail object in main entity
     employee.setEmpDetails(detail);
     //save employee
     session.save(employee);
     //clean up resources
     session.flush();
     session.close();
     sessionFactory.close();
   }
}

Output

When this code is executed, we can see the below logs in console.
Logs demonstrate that an insert query is executed.
The query has all attributes of both the classes combined together and is executed on the Employee table only.

Hibernate: create table Employee (id integer not null, age integer not null, bloodGroup varchar(255), country varchar(255), employeeId varchar(255), name varchar(255), primary key (id))

Before executing the code, place a file Hibernate.cfg.xml with following contents in the classpath.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/employee</property>
    <property name="connection.username">root</property>
    <property name="connection.password">root</property>
    <property name="show_sql">true</property>
    <property name="hbm2ddl.auto">update</property>
  </session-factory>
</hibernate-configuration>

This is the configuration file required by Hibernate which tells it the details of the database to which Hibernate should connect and perform operations.

Let’s tweak in

  1. If neither @Embedded annotation is applied over the sub-entity class and @Embedded annotation is applied over the field the sub-entity class inside main entity class, then following error is raised

    Exception in thread “main” org.hibernate.MappingException: Could not determine type for: EmployeeDetail, at table: Employee, for columns: [org.hibernate.mapping.Column(empDetails)]

  2. If Hibernate.cfg.xml is not found in the classpath, then the following error will be raised.

    Exception in thread “main” org.hibernate.internal.util.config.ConfigurationException: Could not locate cfg.xml resource [hibernate.cfg.xml]

  3. It is not mandatory to name the configuration file as Hibernate.cfg.xml.
    You can name it as per your choice but you need to inform Hibernate about it.
    This is done by providing the file name in the configure method of org.Hibernate.cfg.Configuration class as new Configuration().configure("fileName")
    More on Hibernate configuration here.
Don’t forget to hit the clap if the article was useful.

Leave a Reply