How do I use @Embeddable and @Embedded classes in Hibernate?

In Hibernate (or JPA in general), the @Embeddable and @Embedded annotations are used to help manage object-relational mapping for complex types (value types) within entities. These annotations allow you to define reusable and nested objects that don’t require their own database table but are saved as part of the owning entity.

Here’s how to use them:

  1. Define an embeddable class:
    A class annotated with @Embeddable is a value type that can be embedded in an entity. It cannot exist independently in the database — its lifecycle is tied to the owning entity.
  2. Use the @Embedded annotation in the entity:
    The @Embedded annotation is used to include an instance of the embeddable class in an entity.
  3. Mapping fields of the @Embeddable class:
    The fields of the embeddable class are mapped to columns in the table of the owning entity.

Example Usage:

Step 1: Define the Embeddable Class

package org.kodejava.hibernate;

import jakarta.persistence.Embeddable;

@Embeddable
public class Address {
    private String street;
    private String city;
    private String state;
    private String zipCode;

    // Constructors, getters, and setters
    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 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; }
}

Step 2: Use the @Embedded Class in an Entity

package org.kodejava.hibernate;

import jakarta.persistence.*;

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

    private String name;

    @Embedded
    private Address address;

    // Constructors, getters, and setters
    public Employee() {}

    public Employee(String name, Address address) {
        this.name = name;
        this.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; }
}

Step 3: Saving and Retrieving Data

Here is how you can interact with the entity and the embeddable in your code:

package org.kodejava.hibernate;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

public class MainApp {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();

        // Create an Address object
        Address address = new Address("123 Main St", "Springfield", "IL", "62704");

        // Create an Employee object with an embedded Address
        Employee employee = new Employee("John Doe", address);

        // Persist the Employee object
        em.persist(employee);

        em.getTransaction().commit();

        // Retrieve and display the Employee and Address information
        Employee retrievedEmployee = em.find(Employee.class, employee.getId());
        System.out.println("Employee Name: " + retrievedEmployee.getName());
        System.out.println("Employee Address: " + retrievedEmployee.getAddress().getStreet());

        em.close();
        emf.close();
    }
}

Step 4: META-INF/persistence.xml

Here is an example of a persistence.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_1.xsd"
             version="3.1">

    <!-- Define a persistence unit -->
    <persistence-unit name="my-persistence-unit" transaction-type="RESOURCE_LOCAL">

        <!-- Specify the entity classes of the project -->
        <class>org.kodejava.hibernate.Employee</class>
        <class>org.kodejava.hibernate.Address</class>

        <!-- Database connection properties -->
        <properties>
            <!-- JDBC connection settings -->
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:test"/>
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="sa"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>

            <!-- Hibernate-specific properties -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/> <!-- Options: create, update, validate, none -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>

</persistence>

Notes:

  1. You can further customize the column names for fields in the embeddable class by using the @AttributeOverrides and @AttributeOverride annotations.

    For example:

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "home_street")),
        @AttributeOverride(name = "city", column = @Column(name = "home_city"))
    })
    private Address homeAddress;
    

    In this example, the street and city fields from Address are mapped to custom column names in the database table.

  2. You can embed the same embeddable class in multiple entities, promoting code reuse.

  3. The embeddable class must have a default (no-arg) constructor because JPA requires it for instantiation.

By using @Embeddable and @Embedded, you can cleanly separate reusable pieces of data and map them effectively to your relational database.


Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>6.4.4.Final</version>
    </dependency>
    <dependency>
        <groupId>jakarta.persistence</groupId>
        <artifactId>jakarta.persistence-api</artifactId>
        <version>3.1.0</version>
    </dependency>
</dependencies>

Maven Central Maven Central

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.