# Basic Types, Enums, Embeddables and Converters — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/m-types

> 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.](assets/figures/hibernate-jpa/section-3-map.svg) — 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.

```java
@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.

**Quiz:** Why is @Enumerated(EnumType.STRING) preferred over the default ORDINAL?

- [x] 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.
