Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Встраиваемые типы

Некоторые поля связаны между собой: email и сайт составляют контакты автора, город и улица — адрес. Такую группу можно вынести в отдельный класс и встроить в сущность. У встраиваемого класса нет своей таблицы и своего id: его поля хранятся обычными столбцами в таблице сущности-владельца. Начиная с Hibernate 6.2 встраиваемым типом может быть запись (record).

Встраиваемый тип

// 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 отмечает класс, который можно встраивать. В отличие от сущности, запись здесь подходит: значение заменяют целиком, менять его по одному полю не нужно.

Сущность Author

// 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;
    }
}

Добавьте <class>com.example.blog.Author</class> в persistence.xml. Запись Contact указывать не нужно — это не сущность.

Сохранение и поиск

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> em.persist(
        new Author("Анна", 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());

В запросе к встроенным полям обращаются через точку: a.contact.email. Вывод:

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=?
Анна: Contact[email=anna@example.com, website=https://anna.example.com]

Столбцы email и website находятся прямо в таблице Author — отдельной таблицы для контактов нет.

Изменение значения

Запись менять нельзя, поэтому новое значение заменяет старое:

// 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()));
});

При фиксации Hibernate видит новый объект и выполняет update Author set email=?,website=?,name=? where id=?.

Переименование столбцов

Имена столбцов берутся из компонентов записи. Чтобы поменять какое-то из них, рядом с @Embedded ставят @AttributeOverride:

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

Теперь в таблице столбец contact_email varchar(255) unique. Тот же приём позволяет встроить один тип дважды, например адрес оплаты и адрес доставки.

Частая ошибка

Если все столбцы встроенного значения равны null, Hibernate читает всё значение как null:

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> em.persist(new Author("Олег", 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

Проверяйте встроенное значение на null, прежде чем им пользоваться.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.