埋め込み可能クラス

埋め込み可能クラスは、エンティティクラスのプロパティをグループ化します。

埋め込み可能クラスの定義

埋め込み可能クラスを定義する方法を示します:

@Embeddable
public class Address {

    final String city;

    final String street;

    @Column(name = "ZIP_CODE")
    final String zip;

    public Address(String city, String street, String zip) {
        this.city = city;
        this.street = street;
        this.zip = zip;
    }
}

クラスとレコードの両方に @Embeddable アノテーションを適用できます。

@Embeddable
public record Address(
  String city,
  String street,
  @Column(name = "ZIP_CODE")String zip) {
}

埋め込み可能クラスはエンティティフィールドの型として使用されます。

@Entity
public class Employee {
    @Id
    Integer id;

    Address address;
}

上記のエンティティ定義は、次のエンティティ定義と同等です。

@Entity
public class Employee {
    @Id
    Integer id;

    String city;

    String street;

    @Column(name = "ZIP_CODE")
    String zip;
}

命名規則

命名規則は、囲んでいるエンティティクラスから継承されます。

フィールドの定義

デフォルトでは、すべてのフィールドは永続的で、データベースまたは結果セットのカラムに対応します。

フィールドの型は次のいずれかである必要があります。

@Embeddable
public class Address {
    String street;
}

カラム

カラム名を @Column アノテーションで指定できます。

@Column(name = "ZIP_CODE")
final String zip;

Transient

埋め込み可能クラスに永続化したくないフィールドがある場合は、@Transient アノテーションを指定できます:

ネストした埋め込み可能クラス

埋め込み可能クラスは他の埋め込み可能クラスをフィールドとして含むことができ、ネストした構成が可能です:

@Embeddable
public class Address {
    String street;
    String city;
    String zipCode;
}

@Embeddable
public class ContactInfo {
    String email;
    String phone;
    Address address; // Nested embeddable
}

@Entity
public class Customer {
    @Id
    Integer id;
    String name;
    ContactInfo contactInfo; // Contains nested Address
}

これにより、Customer エンティティが ContactInfo を含み、さらに ContactInfoAddress を含む階層構造が作成されます。ネストした埋め込み可能クラスのすべてのフィールドは、エンティティのテーブル構造にフラット化されます。

Optionalな埋め込み可能クラス

埋め込み可能クラスを java.util.Optional でラップして、埋め込み可能グループ全体が null になる可能性があることを示すことができます:

@Entity
public class Employee {
    @Id
    Integer id;
    String name;
    Optional<Address> homeAddress;    // Optional embeddable
    Optional<ContactInfo> emergencyContact; // Optional nested embeddable
}

Optionalな埋め込み可能クラスがnullの場合、対応するすべてのデータベースカラムがnullになります。逆に、Optionalな埋め込み可能クラスに対応するすべてのカラムがデータベースでnullの場合、Optionalフィールドは空(Optional.empty())になります。

メソッドの定義

メソッドの使用に制限はありません。

@Embeddedアノテーションの使用

@Embedded アノテーションを使用すると、同じ埋め込み可能型を異なるカラム名プレフィックスで単一のエンティティ内に複数回埋め込むことができます。

基本的な使用方法

@Embeddable
public record Address(String street, String city, String zipCode) {}

@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Customer {
    @Id
    Integer customerId;

    @Embedded(prefix = "billing_")
    Address billingAddress;

    @Embedded(prefix = "shipping_")
    Address shippingAddress;
}

これにより、SQL文で以下のカラムが生成されます:

  • billing_street

  • billing_city

  • billing_zip_code

  • shipping_street

  • shipping_city

  • shipping_zip_code

プレフィックスの動作

prefix 属性はカラム名の生成方法を制御します:

  • カラム名は、プレフィックスと埋め込み可能フィールドのカラム名を組み合わせて生成されます

  • プレフィックスはフィールドのカラム名にそのまま追加されます

  • プレフィックスが指定されていない場合、動作は埋め込み可能フィールドのカラム名を直接使用する場合と同じままです

@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Order {
    @Id
    Integer orderId;

    // Without prefix - generates columns: street, city, zip_code
    Address address;

    // With prefix - generates columns: delivery_street, delivery_city, delivery_zip_code
    @Embedded(prefix = "delivery_")
    Address deliveryAddress;
}

カラムのオーバーライド

columnOverrides 属性を @ColumnOverride アノテーションと共に使用することで、個々のカラムマッピングをきめ細かく制御できます:

@Entity
public class Customer {
    @Id
    Integer id;

    @Embedded(columnOverrides = {
        @ColumnOverride(name = "street", column = @Column(name = "BILLING_STREET")),
        @ColumnOverride(name = "city", column = @Column(name = "BILLING_CITY"))
    })
    Address billingAddress;

    @Embedded(columnOverrides = {
        @ColumnOverride(name = "street", column = @Column(name = "SHIP_STREET", insertable = false)),
        @ColumnOverride(name = "city", column = @Column(name = "SHIP_CITY", updatable = false))
    })
    Address shippingAddress;
}

@ColumnOverride アノテーションを使用すると、以下のことができます:

  • 特定の埋め込み可能フィールドにカスタムカラム名を指定する

  • insertableupdatablequote などのカラム属性をオーバーライドする

  • 両方が指定されている場合、prefix 属性よりも優先される

注釈

prefixcolumnOverrides の両方が使用されている場合、指定されたフィールドに対しては @ColumnOverride の設定が優先されます。

void doSomething() {
    Employee employee = new Employee(); // Entity
    Address address = new Address("Tokyo", "Yaesu", "103-0028"); // Embeddable
    employee.setAddress(address);
}