Careeroza
Enums and Transient Fields
Mapping Enums
public enum ProductStatus { ACTIVE, OUT_OF_STOCK, DISCONTINUED }
@Entity
@Table(name = "products")
public class Product {
// ...
@Enumerated(EnumType.STRING) // store the NAME, not the position
@Column(length = 20, nullable = false)
private ProductStatus status = ProductStatus.ACTIVE;
}-- EnumType.STRING (PostgreSQL / H2)
status varchar(20) not null check (status in ('ACTIVE','OUT_OF_STOCK','DISCONTINUED'))
-- EnumType.ORDINAL (the default if you only write the field!)
status smallint not null check (status between 0 and 2)Note: On MySQL, Hibernate 6.2+ maps EnumType.STRING to a native column type enum('ACTIVE','OUT_OF_STOCK','DISCONTINUED').
Why ORDINAL Is Dangerous
Example
ORDINAL stores the position (0, 1, 2). If a developer later inserts a new constant in the middle, every existing row silently changes meaning:
import java.util.List;
public class Main {
public static void main(String[] args) {
// Version 1 of the app saved these ordinals
List<Integer> storedOrdinals = List.of(
StatusV1.ACTIVE.ordinal(), StatusV1.OUT_OF_STOCK.ordinal(), StatusV1.DISCONTINUED.ordinal());
System.out.println("Stored in DB: " + storedOrdinals);
// Version 2 added PRE_ORDER in the middle of the enum
for (int i = 0; i < storedOrdinals.size(); i++) {
int ord = storedOrdinals.get(i);
System.out.printf("row %d: saved as %-13s read back as %s%n",
i + 1, StatusV1.values()[ord], StatusV2.values()[ord]);
}
// With EnumType.STRING the name survives the change
String stored = StatusV1.DISCONTINUED.name();
System.out.println("STRING mapping reads back: " + StatusV2.valueOf(stored));
}
}
enum StatusV1 { ACTIVE, OUT_OF_STOCK, DISCONTINUED }
enum StatusV2 { ACTIVE, PRE_ORDER, OUT_OF_STOCK, DISCONTINUED }Output
Stored in DB: [0, 1, 2] row 1: saved as ACTIVE read back as ACTIVE row 2: saved as OUT_OF_STOCK read back as PRE_ORDER row 3: saved as DISCONTINUED read back as OUT_OF_STOCK STRING mapping reads back: DISCONTINUED
Tip: Always write @Enumerated(EnumType.STRING). It costs a few bytes per row and saves you from silent data corruption.
@Transient: Fields That Are Not Stored
@Entity
@Table(name = "cart_items")
public class CartItem {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long productId;
private Integer quantity;
private BigDecimal unitPrice;
@Transient // no column; calculated in Java
private BigDecimal lineTotal;
@Transient
public BigDecimal getLineTotal() {
return unitPrice.multiply(BigDecimal.valueOf(quantity));
}
}Use jakarta.persistence.Transient for derived values, temporary UI flags, or values loaded from another service. The Java keyword transient also excludes a field from persistence, but it also excludes it from Java serialization, so the annotation is the clearer choice.