पाठ 7 / 25

Basic Types, Enums, Embeddables and Converters

Map Java types, enums, value objects and custom conversions.

Mapping more than strings and numbers

JPA maps common Java types automatically: primitives and wrappers, String, BigDecimal, and the java.time types (LocalDate, LocalDateTime, Instant, OffsetDateTime). Enums are stored as ordinals by default (EnumType.ORDINAL), which silently corrupts data if anyone reorders or inserts enum constants, so always use @Enumerated(EnumType.STRING). Embeddables model value objects without their own identity: an @Embeddable Address with street, city and pin code is stored in the owning entity's table, mapped with @Embedded (and @AttributeOverrides to rename columns when embedding twice). Attribute converters (@Converter implementing AttributeConverter<X, Y>) map custom types such as a Money value object or an encrypted string to a database column. Hibernate 6 and later also map JSON columns with @JdbcTypeCode(SqlTypes.JSON), and large text or binary data with @Lob. Use @Column(columnDefinition = ...) sparingly, since it ties mappings to one database.

Value objects inside an entity row

An embeddable's fields become columns of the owning entity's table.

A wide table row divided into columns, with a bracketed group of three columns highlighted and linked to a small nested box above.
Figure 3.1 — An embedded value object flattened into columns.

Enum, embeddable and converter mappings

Each mapping keeps the domain model expressive and the schema stable.

@Embeddable
public class Address {
    private String street;
    private String city;
    @Column(name = "pin_code", length = 6)
    private String pinCode;
    protected Address() { }
    public Address(String street, String city, String pinCode) { /* validate and assign */ }
}

public enum OrderStatus { PLACED, PAID, SHIPPED, CANCELLED }

@Converter(autoApply = false)
public class PhoneConverter implements AttributeConverter<PhoneNumber, String> {
    public String convertToDatabaseColumn(PhoneNumber p) { return p == null ? null : p.e164(); }
    public PhoneNumber convertToEntityAttribute(String s) { return s == null ? null : PhoneNumber.parse(s); }
}

@Entity
public class CustomerOrder {
    @Id @GeneratedValue private Long id;

    @Enumerated(EnumType.STRING)                 // stores 'PAID', not 1
    @Column(nullable = false, length = 16)
    private OrderStatus status;

    @Embedded
    @AttributeOverride(name = "street", column = @Column(name = "ship_street"))
    @AttributeOverride(name = "city", column = @Column(name = "ship_city"))
    @AttributeOverride(name = "pinCode", column = @Column(name = "ship_pin"))
    private Address shippingAddress;

    @Convert(converter = PhoneConverter.class)
    private PhoneNumber contactPhone;

    @JdbcTypeCode(SqlTypes.JSON)                  // Hibernate 6+: JSON column
    private Map<String, String> giftOptions;

    private Instant placedAt;
}

Never use EnumType.ORDINAL

Adding REFUNDED between PAID and SHIPPED shifts the ordinals, so every existing row silently changes meaning. String-mapped enums survive reordering and are readable in the database.

त्वरित जाँच: Why is @Enumerated(EnumType.STRING) preferred over the default ORDINAL?

  • Ordinals change meaning when enum constants are reordered or inserted, corrupting existing data
  • Strings are faster to compare
  • ORDINAL is not supported by Hibernate
  • STRING uses less storage
Answer

Ordinals change meaning when enum constants are reordered or inserted, corrupting existing data — Stored ordinals depend on declaration order, which changes as the code evolves.