Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Embedded types

Some fields belong together: an email and a website make up the contact details of an author, a city and a street make up an address. Such a group can be put into a separate class and embedded into an entity. The embedded class has no table and no id of its own: its fields are stored as ordinary columns of the owning entity's table. Since Hibernate 6.2 an embedded type can be a record.

The embedded type

// src/main/java/com/example/blog/Contact.java
package com.example.blog;
 
import jakarta.persistence.Embeddable;
 
@Embeddable
public record Contact(String email, String website) {
}

@Embeddable marks a class that can be embedded. Unlike an entity, a record is fine here: the value is replaced as a whole, so it does not need to be changed field by field.

The Author entity

// src/main/java/com/example/blog/Author.java
package com.example.blog;
 
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
 
@Entity
public class Author {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
    @Embedded
    private Contact contact;
 
    protected Author() {
    }
 
    public Author(String name, Contact contact) {
        this.name = name;
        this.contact = contact;
    }
 
    public String getName() {
        return name;
    }
 
    public Contact getContact() {
        return contact;
    }
 
    public void setContact(Contact contact) {
        this.contact = contact;
    }
}

Add <class>com.example.blog.Author</class> to persistence.xml. The Contact record does not need to be listed — it is not an entity.

Saving and searching

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> em.persist(
        new Author("Anna", new Contact("anna@example.com", "https://anna.example.com"))));
 
Author author = emf.callInTransaction(em -> em
        .createQuery("select a from Author a where a.contact.email = :email", Author.class)
        .setParameter("email", "anna@example.com")
        .getSingleResult());
 
IO.println(author.getName() + ": " + author.getContact());

In a query the embedded fields are reached through a dot: a.contact.email. Output:

Hibernate: create table Author (id bigint not null, email varchar(255), name varchar(255), website varchar(255), primary key (id))
Hibernate: insert into Author (email,website,name,id) values (?,?,?,?)
Hibernate: select a1_0.id,a1_0.email,a1_0.website,a1_0.name from Author a1_0 where a1_0.email=?
Anna: Contact[email=anna@example.com, website=https://anna.example.com]

The table has the email and website columns directly — there is no separate table for contacts.

Changing the value

A record cannot be changed, so a new value replaces the old one:

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> {
    Author author = em.find(Author.class, 1L);
    author.setContact(new Contact("anna@example.org", author.getContact().website()));
});

At commit Hibernate sees the new object and runs update Author set email=?,website=?,name=? where id=?.

Renaming columns

The column names come from the record components. To change one of them, put @AttributeOverride next to @Embedded:

// src/main/java/com/example/blog/Author.java
@Embedded
@AttributeOverride(name = "email", column = @Column(name = "contact_email", unique = true))
private Contact contact;

The table now has contact_email varchar(255) unique. The same technique lets you embed one type twice, for example a billing and a shipping address.

Common mistake

When all columns of an embedded value are null, Hibernate reads the whole value back as null:

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> em.persist(new Author("Oleg", new Contact(null, null))));
Author oleg = emf.callInTransaction(em -> em.find(Author.class, 1L));
IO.println(oleg.getContact().email());
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.example.blog.Contact.email()" because the return value of "com.example.blog.Author.getContact()" is null

Check the embedded value for null before using it.

Thanks for reading!

Donate to the site — we work hard for you.