← Назад к вопросам

Что такое Embedded?

1.2 Junior🔥 111 комментариев
#ORM и Hibernate#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое @Embedded

@Embedded — это аннотация из JPA (Java Persistence API), которая используется для встраивания Value Objects в Entity. Позволяет создавать сложные типы данных без создания отдельных таблиц в базе данных. Это инструмент для чистого моделирования бизнес-логики в DDD.

Как это работает

Обычно каждый Entity соответствует одной таблице в БД. Но Value Objects могут быть встроены в таблицу Entity'а:

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String customerName;
    
    @Embedded
    private Money totalAmount;  // Встраиваем Value Object
    
    @Embedded
    private Address shippingAddress;  // Ещё один Value Object
}

В таблице БД это выглядит так:

CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    customer_name VARCHAR(255),
    total_amount_value DECIMAL(19, 2),  -- Поля из Money
    total_amount_currency VARCHAR(3),
    shipping_address_street VARCHAR(255),  -- Поля из Address
    shipping_address_city VARCHAR(255),
    shipping_address_postal_code VARCHAR(10)
);

Пример: Value Object Money

@Embeddable  // Отмечаем, что это можно встраивать
public class Money {
    @Column(name = "amount", precision = 19, scale = 2)
    private BigDecimal amount;
    
    @Column(name = "currency", length = 3)
    private String currency;
    
    public Money(BigDecimal amount, String currency) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidMoneyException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }
    
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new CurrencyMismatchException();
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
    
    public Money subtract(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new CurrencyMismatchException();
        }
        return new Money(this.amount.subtract(other.amount), this.currency);
    }
}

@Embeddable аннотация указывает, что класс можно встраивать в другие Entity'ы.

Пример: Value Object Address

@Embeddable
public class Address {
    @Column(name = "street")
    private String street;
    
    @Column(name = "city")
    private String city;
    
    @Column(name = "postal_code")
    private String postalCode;
    
    @Column(name = "country")
    private String country;
    
    public Address(String street, String city, String postalCode, String country) {
        if (street == null || street.isBlank()) {
            throw new InvalidAddressException("Street cannot be empty");
        }
        if (city == null || city.isBlank()) {
            throw new InvalidAddressException("City cannot be empty");
        }
        this.street = street;
        this.city = city;
        this.postalCode = postalCode;
        this.country = country;
    }
    
    public String getFullAddress() {
        return String.format("%s, %s, %s, %s", street, city, postalCode, country);
    }
}

Использование @Embedded в Entity

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "billing_street")),
        @AttributeOverride(name = "city", column = @Column(name = "billing_city"))
    })
    private Address billingAddress;
    
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "shipping_street")),
        @AttributeOverride(name = "city", column = @Column(name = "shipping_city"))
    })
    private Address shippingAddress;
    
    @Embedded
    private Money totalAmount;
    
    private LocalDateTime createdAt;
    
    public Order(Address billingAddress, Address shippingAddress, Money totalAmount) {
        this.billingAddress = billingAddress;
        this.shippingAddress = shippingAddress;
        this.totalAmount = totalAmount;
        this.createdAt = LocalDateTime.now();
    }
    
    public void updateTotalAmount(Money newAmount) {
        if (!newAmount.getCurrency().equals(this.totalAmount.getCurrency())) {
            throw new CurrencyMismatchException();
        }
        this.totalAmount = newAmount;
    }
}

@AttributeOverrides позволяет переименовать столбцы при встраивании одного Value Object несколько раз.

Различие: @Embedded vs @ElementCollection

@Embedded — для одного Value Object:

@Embedded
private Address shippingAddress;  // Один адрес

@ElementCollection — для коллекции Value Objects:

@ElementCollection
@CollectionTable(name = "order_items")
private List<OrderItem> items;  // Несколько OrderItem'ов

Пример: коллекция встроенных объектов

@Embeddable
public class OrderItem {
    @Column(name = "product_id")
    private Long productId;
    
    @Column(name = "quantity")
    private int quantity;
    
    @Embedded
    private Money price;
}

@Entity
@Table(name = "orders")
public class Order {
    @Id
    private Long id;
    
    @ElementCollection(fetch = FetchType.LAZY)
    @CollectionTable(name = "order_items", joinColumns = @JoinColumn(name = "order_id"))
    private List<OrderItem> items = new ArrayList<>();
}

В БД это создаст таблицу:

CREATE TABLE order_items (
    order_id BIGINT,
    product_id BIGINT,
    quantity INT,
    price_value DECIMAL(19, 2),
    price_currency VARCHAR(3),
    FOREIGN KEY (order_id) REFERENCES orders(id)
);

Правила для @Embeddable

  1. Конструктор без параметров обязателен (может быть protected):
@Embeddable
public class Money {
    private BigDecimal amount;
    private String currency;
    
    // Обязателен для Hibernate
    protected Money() {}
    
    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }
}
  1. Должен быть сериализуемым (опционально, но рекомендуется):
@Embeddable
public class Money implements Serializable {
    private static final long serialVersionUID = 1L;
    // ...
}
  1. Поля должны быть простыми типами или другими @Embeddable:
@Embeddable
public class Address {
    private String street;  // ✅ OK
    private String city;    // ✅ OK
    private Money cost;     // ✅ OK (другой Embeddable)
    // private Order order; // ❌ NO - не может содержать Entity
}

Плюсы @Embedded

Моделирование Value Objects — соответствует DDD ✅ Чистый код — бизнес-логика отделена ✅ Нет отдельных таблиц — оптимизация БД ✅ Переиспользование — один Value Object в разных Entity'ях ✅ Валидация — бизнес-правила в конструкторе Value Object

Минусы @Embedded

Нельзя искать по Value Object — нельзя написать findByAddressНельзя использовать в запросах — сложнее JPQL ❌ Большие таблицы — много столбцов в одной таблице

Пример: полный Order с Embedded

@Entity
public class Order {
    @Id
    private Long id;
    
    @Embedded
    private Money totalAmount;
    
    @Embedded
    @AttributeOverrides(@AttributeOverride(name = "street", column = @Column(name = "billing_street")))
    private Address billingAddress;
    
    @Embedded
    @AttributeOverrides(@AttributeOverride(name = "street", column = @Column(name = "shipping_street")))
    private Address shippingAddress;
    
    @ElementCollection
    @CollectionTable(name = "order_items")
    private List<OrderItem> items;
    
    public void addItem(OrderItem item) {
        if (items == null) {
            items = new ArrayList<>();
        }
        items.add(item);
        updateTotalAmount();
    }
    
    private void updateTotalAmount() {
        Money total = new Money(BigDecimal.ZERO, "USD");
        for (OrderItem item : items) {
            total = total.add(item.getPrice());
        }
        this.totalAmount = total;
    }
}

@Embedded — это элегантный способ работать с Value Objects, сохраняя чистоту архитектуры и не усложняя схему БД.

Что такое Embedded? | PrepBro