Embeddable types

Historically Hibernate called these components. JPA calls them embeddables. Either way the concept is the same: a composition of values.

For example we might have a Publisher class that is a composition of name and country, or a Location class that is a composition of country and city.

Usage of the word embeddable

To avoid any confusion with the annotation that marks a given embeddable type, the annotation will be further referred as @Embeddable.

Throughout this chapter and thereafter, for brevity sake, embeddable types may also be referred as embeddable.

Example 1. Embeddable type example
@Embeddable
public static class Publisher {

    private String name;

    private Location location;

    public Publisher(String name, Location location) {
        this.name = name;
        this.location = location;
    }

    private Publisher() {}

    //Getters and setters are omitted for brevity
}

@Embeddable
public static class Location {

    private String country;

    private String city;

    public Location(String country, String city) {
        this.country = country;
        this.city = city;
    }

    private Location() {}

    //Getters and setters are omitted for brevity
}

An embeddable type is another form of value type, and its lifecycle is bound to a parent entity type, therefore inheriting the attribute access from its parent (for details on attribute access, see Access strategies).

Embeddable types can be made up of basic values as well as associations, with the caveat that, when used as collection elements, they cannot define collections themselves.

Component / Embedded

Most often, embeddable types are used to group multiple basic type mappings and reuse them across several entities.

Example 2. Simple Embeddedable
@Entity(name = "Book")
public static class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String author;

    private Publisher publisher;

    //Getters and setters are omitted for brevity
}

@Embeddable
public static class Publisher {

    @Column(name = "publisher_name")
    private String name;

    @Column(name = "publisher_country")
    private String country;

    public Publisher(String name, String country) {
        this.name = name;
        this.country = country;
    }

    private Publisher() {}

    //Getters and setters are omitted for brevity
}
create table Book (
    id bigint not null,
    author varchar(255),
    publisher_country varchar(255),
    publisher_name varchar(255),
    title varchar(255),
    primary key (id)
)

JPA defines two terms for working with an embeddable type: @Embeddable and @Embedded.

@Embeddable is used to describe the mapping type itself (e.g. Publisher).

@Embedded is for referencing a given embeddable type (e.g. book#publisher).

So, the embeddable type is represented by the Publisher class and the parent entity makes use of it through the book#publisher object composition.

The composed values are mapped to the same table as the parent table. Composition is part of good Object-oriented data modeling (idiomatic Java). In fact, that table could also be mapped by the following entity type instead.

Example 3. Alternative to embeddable type composition
@Entity(name = "Book")
public static class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String author;

    @Column(name = "publisher_name")
    private String publisherName;

    @Column(name = "publisher_country")
    private String publisherCountry;

    //Getters and setters are omitted for brevity
}

The composition form is certainly more Object-oriented, and that becomes more evident as we work with multiple embeddable types.

Multiple embeddable types

Although from an object-oriented perspective, it’s much more convenient to work with embeddable types, this example doesn’t work as-is. When the same embeddable type is included multiple times in the same parent entity type, the JPA specification demands setting the associated column names explicitly.

This requirement is due to how object properties are mapped to database columns. By default, JPA expects a database column having the same name with its associated object property. When including multiple embeddables, the implicit name-based mapping rule doesn’t work anymore because multiple object properties could end-up being mapped to the same database column.

We have a few options to handle this issue.

Overriding Embeddable types

JPA defines the @AttributeOverride annotation to handle this scenario. This way, the mapping conflict is resolved by setting up explicit name-based property-column type mappings.

If an Embeddabe type is used multiple times in some entity, you need to use the @AttributeOverride and @AssociationOverride annotations to override the default column names definied by the Embeddable.

Considering you have the following Publisher embeddable type which defines a @ManyToOne association with the Country entity:

Example 4. Embeddable type with a @ManyToOne association
@Embeddable
public static class Publisher {

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Country country;

    public Publisher(String name, Country country) {
        this.name = name;
        this.country = country;
    }

    private Publisher() {}

    //Getters and setters are omitted for brevity
}

@Entity(name = "Country")
public static class Country {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String name;

    //Getters and setters are omitted for brevity
}
create table Country (
    id bigint not null,
    name varchar(255),
    primary key (id)
)

alter table Country
    add constraint UK_p1n05aafu73sbm3ggsxqeditd
    unique (name)

Now, if you have a Book entity which declares two Publisher embeddable types for the ebook and paperback version, you cannot use the default Publisher embeddable mapping since there will be a conflict between the two embeddable column mappings.

Therefore, the Book entity needs to override the embeddable type mappings for each Publisher attribute:

Example 5. Overriding embeddable type attributes
@Entity(name = "Book")
@AttributeOverrides({
    @AttributeOverride(
        name = "ebookPublisher.name",
        column = @Column(name = "ebook_publisher_name")
    ),
    @AttributeOverride(
        name = "paperBackPublisher.name",
        column = @Column(name = "paper_back_publisher_name")
    )
})
@AssociationOverrides({
    @AssociationOverride(
        name = "ebookPublisher.country",
        joinColumns = @JoinColumn(name = "ebook_publisher_country_id")
    ),
    @AssociationOverride(
        name = "paperBackPublisher.country",
        joinColumns = @JoinColumn(name = "paper_back_publisher_country_id")
    )
})
public static class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String author;

    private Publisher ebookPublisher;

    private Publisher paperBackPublisher;

    //Getters and setters are omitted for brevity
}
create table Book (
    id bigint not null,
    author varchar(255),
    ebook_publisher_name varchar(255),
    paper_back_publisher_name varchar(255),
    title varchar(255),
    ebook_publisher_country_id bigint,
    paper_back_publisher_country_id bigint,
    primary key (id)
)

alter table Book
    add constraint FKm39ibh5jstybnslaoojkbac2g
    foreign key (ebook_publisher_country_id)
    references Country

alter table Book
    add constraint FK7kqy9da323p7jw7wvqgs6aek7
    foreign key (paper_back_publisher_country_id)
    references Country

Embeddables and ImplicitNamingStrategy

This is a Hibernate specific feature. Users concerned with JPA provider portability should instead prefer explicit column naming with @AttributeOverride.

Hibernate naming strategies are covered in detail in Naming. However, for the purposes of this discussion, Hibernate has the capability to interpret implicit column names in a way that is safe for use with multiple embeddable types.

Example 6. Implicit multiple embeddable type mapping
@Entity(name = "Book")
public static class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String author;

    private Publisher ebookPublisher;

    private Publisher paperBackPublisher;

    //Getters and setters are omitted for brevity
}

@Embeddable
public static class Publisher {

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Country country;

    public Publisher(String name, Country country) {
        this.name = name;
        this.country = country;
    }

    private Publisher() {}

    //Getters and setters are omitted for brevity
}

@Entity(name = "Country")
public static class Country {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String name;

    //Getters and setters are omitted for brevity
}

To make it work, you need to use the ImplicitNamingStrategyComponentPathImpl naming strategy.

Example 7. Enabling implicit embeddable type mapping using the component path naming strategy
metadataBuilder.applyImplicitNamingStrategy(
    ImplicitNamingStrategyComponentPathImpl.INSTANCE
);

Now the "path" to attributes are used in the implicit column naming:

create table Book (
    id bigint not null,
    author varchar(255),
    ebookPublisher_name varchar(255),
    paperBackPublisher_name varchar(255),
    title varchar(255),
    ebookPublisher_country_id bigint,
    paperBackPublisher_country_id bigint,
    primary key (id)
)

You could even develop your own naming strategy to do other types of implicit naming strategies.

Collections of embeddable types

Collections of embeddable types are specifically value collections (as embeddable types are a value type). Value collections are covered in detail in Collections of value types.

Embeddable types as Map key

Embeddable types can also be used as Map keys. This topic is converted in detail in Map - key.

Embeddable types as identifiers

Embeddable types can also be used as entity type identifiers. This usage is covered in detail in Composite identifiers.

Embeddable types that are used as collection entries, map keys or entity type identifiers cannot include their own collection mappings.