SkillByAIOpen interactive version →

Lesson 3 / 25

Your First Entity

Map a class with @Entity, @Id, @GeneratedValue, @Table and @Column.

Mapping a class to a table

An entity is a class whose instances are persisted. Requirements: annotate it with @Entity; give it a primary key field with @Id; provide a no-argument constructor (it may be protected); and do not make the class or persistent methods final if you rely on lazy-loading proxies. By default the table and column names derive from the class and field names (Spring Boot's naming strategy converts firstName to first_name); use @Table(name = ...) and @Column(name = ..., nullable = false, length = ..., unique = ...) to be explicit. @GeneratedValue asks the provider to generate IDs (strategies are covered later). Fields that should not be persisted are marked @Transient. JPA can access state through fields (when @Id is on a field) or properties (getters); field access is the common modern choice. Entities are not plain data bags: keep invariants in constructors and domain methods, and expose only the setters you need. Java records cannot be entities, because entities must be mutable and proxyable, but records work well as DTOs and embeddables in recent versions.

A product entity

Explicit table and column mapping, a protected constructor for JPA and a domain method.

import jakarta.persistence.*;
import java.math.BigDecimal;

@Entity
@Table(name = "products", uniqueConstraints = @UniqueConstraint(columnNames = "sku"))
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
    @SequenceGenerator(name = "product_seq", sequenceName = "products_seq", allocationSize = 50)
    private Long id;

    @Column(nullable = false, length = 32)
    private String sku;

    @Column(nullable = false, length = 200)
    private String title;

    @Column(nullable = false, precision = 12, scale = 2)
    private BigDecimal price;

    @Transient
    private boolean recentlyViewed;          // not stored

    protected Product() { }                   // for JPA

    public Product(String sku, String title, BigDecimal price) {
        this.sku = sku;
        this.title = title;
        changePrice(price);
    }

    public void changePrice(BigDecimal newPrice) {
        if (newPrice.signum() < 0) throw new IllegalArgumentException("price must be >= 0");
        this.price = newPrice;
    }

    public Long getId() { return id; }
    public String getSku() { return sku; }
    public BigDecimal getPrice() { return price; }
}

Use BigDecimal for money

Mapping prices to double causes rounding errors such as 0.1 + 0.2 ≠ 0.3. Use BigDecimal with an explicit precision and scale, matching a NUMERIC(12,2) column.

Quick check: Which is required for a JPA entity class?

  • A public all-arguments constructor only
  • Every field marked @Column
  • An @Id field and a no-argument constructor (which may be protected)
  • The class must be a record
Answer

An @Id field and a no-argument constructor (which may be protected) — Entities need a primary key and a no-arg constructor that the provider can call.