Entity Mapping & Annotations

@Entity, @Id, @GeneratedValue, @Column, @Table, and mapping relationships between entities.

@Entity and @Table

@Entity is the minimum required to make a class persistent; @Table is optional and only needed when the table name should differ from Hibernate's default (the class's simple name):

Java
@Entity
@Table(name = "products", schema = "inventory")
public class Product {
    // ...
}

Without @Table, Hibernate would map this class to a table literally named Product (or product, depending on the naming strategy configured) — explicit naming avoids surprises and keeps table names under your control as the schema evolves independently of class names.

@Id and @GeneratedValue

Every entity needs exactly one identifier field, marked @Id. @GeneratedValue delegates the actual value generation to a strategy:

Java
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
Strategy How it works
IDENTITY Delegates to the database's auto-increment column — simple, but Hibernate can't batch inserts efficiently since each row's ID isn't known until after the insert
SEQUENCE Uses a database sequence object to pre-allocate IDs — supports batching, generally the preferred strategy on databases that support sequences (PostgreSQL, Oracle)
AUTO Lets Hibernate pick a strategy appropriate for the configured database dialect
TABLE Simulates a sequence using an ordinary table — portable but slower; rarely used today

@Column

@Column fine-tunes how a field maps to its column — name, nullability, uniqueness, length, precision:

Java
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "product_name", nullable = false, length = 200)
    private String name;

    @Column(nullable = false, unique = true)
    private String sku;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    @Column(updatable = false)
    private LocalDateTime createdAt;
}

A field with no @Column at all is still mapped — Hibernate infers a column name from the field name and reasonable defaults for everything else. @Column is for the cases where a default isn't good enough: enforcing NOT NULL/UNIQUE at the schema level, capping a VARCHAR length, or marking a column immutable after creation (updatable = false, useful for a createdAt timestamp).

Enums, dates, and other common field types

Java
@Entity
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    @Column(nullable = false)
    private LocalDateTime createdAt;

    @Lob
    private String notes;
}

@Enumerated(EnumType.STRING) stores the enum's name ("SHIPPED") rather than its ordinal position — always prefer STRING over the default ORDINAL, because reordering enum constants would otherwise silently corrupt already-stored data (ordinal 0 meaning something different after a new constant is inserted at the top of the enum). @Lob marks a field for large object storage (a CLOB/TEXT column, in this case).

Mapping relationships

Java
@Entity
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
}

@JoinColumn names the actual foreign key column on the owning side of the relationship (customer_id); mappedBy on the inverse side (here, on OrderItem's order field, referenced by name) tells Hibernate this side doesn't own the foreign key and shouldn't create one — it's purely a query-time reference. Relationship mapping in depth (fetch strategies, cascading, @ManyToMany) is covered together with Spring Data JPA's own relationship page, since both frameworks share the exact same JPA annotations.

Common mistakes

  • Using @Enumerated(EnumType.ORDINAL) (the JPA default when @Enumerated is omitted or unqualified) — a reordered or inserted enum constant silently changes the meaning of every already-stored row.
  • Omitting @Column(nullable = false) for fields that are genuinely required, relying only on Java-level null checks — the database itself should enforce the same constraint, since not every write necessarily goes through your application code (migrations, other services, manual fixes).
  • Not setting an explicit @Table name and being surprised by whatever naming strategy convention was configured (or defaulted) for the project.
  • Forgetting that @JoinColumn belongs on the owning side of a relationship, and mappedBy on the inverse side — put them on the wrong sides and Hibernate either creates duplicate/unexpected columns or fails to map the relationship at all.