That VARCHAR status column is an enum in denial
Every schema has a status VARCHAR(30). It starts innocent: three
possible values, everyone knows what they are. A year later it holds
'PAID', 'paid', 'Pago' and a
'PAIDD' nobody can explain.
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
status VARCHAR(30) NOT NULL
);
A short text column with a state-like name (status,
type, role...) is almost always an enum in disguise.
The database accepts any string, so integrity comes down to the team's
discipline. Discipline doesn't scale.
The options, lightest to heaviest
- CHECK constraint:
status VARCHAR(30) NOT NULL CHECK (status IN ('PENDING','PAID','CANCELLED')). Cheap, readable, and adding a new value is anALTER TABLE ... DROP/ADD CONSTRAINTwithout rewriting the table. - Native ENUM type:
CREATE TYPE order_status AS ENUM (...). More compact on disk and self-documenting, butALTER TYPE ... ADD VALUEhas restrictions (it can't run inside a transaction on older versions) and removing a value isn't a thing: you recreate the type. - Lookup table: when the values change at runtime or carry data with them (description, display order). An FK instead of a string.
For most CRUD apps, a CHECK constraint does the job. A native ENUM pays off when the set is genuinely stable.
On the Java side
public enum OrderStatus { PENDING, PAID, CANCELLED }
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 30)
private OrderStatus status;
EnumType.STRING always. The JPA default is
ORDINAL, which stores the enum's position; reordering the constants
in the class silently corrupts all your historical data.