JPA Entities & Mapping

Reviewed & published by Brayan K

A JPA entity is a plain Java class that maps to a database table. With a handful of annotations — @Entity, @Id, @GeneratedValue, @Column, @Table — you describe how objects become rows and back again.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

Before You Start

You should understand classes and objects and the basics of Spring Data JPA. These examples use the jakarta.persistence annotations.

What You'll Learn

The Big Idea — A Form That Fills a Spreadsheet Row

💡 Analogy: Think of an @Entity class as a paper form and the database table as a spreadsheet. Each field on the form is a column; each filled-in form becomes one row. @Id is the row number, @GeneratedValue means the spreadsheet stamps that number for you, and @Column notes are the rules ("this box can't be blank, max 100 characters"). You design the form once; the framework files every submission as a tidy row.

Object-relational mapping is just this translation: objects to rows, rows to objects, automatically.

1️⃣ A Basic Entity

Annotate a class with @Entity, mark its key with @Id, and let the database generate ids. @Table and @Column fine-tune the names and constraints.

import jakarta.persistence.*;

// @Entity marks this class as a JPA-managed table mapping.
// @Table lets you name the table explicitly (defaults to "Customer").
@Entity
@Table(name = "customers")
public class Customer {

    @Id                                              // primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // DB auto-increment
    private Long id;

    @Column(nullable = false, length = 100)          // NOT NULL VARCHAR(100)
    private String name;

    @Column(name = "email_address", unique = true)   // custom column name + UNIQUE
    private String email;

    // JPA requires a no-arg constructor (the provider builds instances reflectively).
    protected Customer() { }

    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public Long getId()    { return id; }
    public String getName()  { return name; }
    public String getEmail() { return email; }
}
-- The mapping above produces schema roughly like this (Hibernate DDL):

CREATE TABLE customers (
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    name          VARCHAR(100) NOT NULL,
    email_address VARCHAR(255),
    PRIMARY KEY (id),
    UNIQUE (email_address)
);

2️⃣ How a Generated Id Behaves

With @GeneratedValue, the id is null until the row is inserted; the database then assigns the value. Here is a plain-Java simulation of that lifecycle you can actually run.

// PLAIN-JAVA illustration of how an auto-generated id behaves:
// before persisting, id is null; the database assigns it on insert.

public class Main {
    static class Customer {
        Long id;               // null until "saved"
        String name;
        Customer(String name) { this.name = name; }
    }

    // Simulate a database assigning sequential ids on insert.
    static long nextId = 1;
    static void save(Customer c) { c.id = nextId++; }

    public static void main(String[] args) {
        Customer a = new Customer("Ana");
        Customer b = new Customer("Ben");
        System.out.println("before save, a.id = " + a.id);
        save(a);
        save(b);
        System.out.println("after save,  a.id = " + a.id);
        System.out.println("after save,  b.id = " + b.id);
    }
}

3️⃣ Keeping Fields Out — @Transient

JPA persists every field by default. A value you only compute in memory — a formatted price, a cached total — should be marked @Transient so it never becomes a column.

import jakarta.persistence.*;

@Entity
public class Product {

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

    @Column(nullable = false)
    private String name;

    @Column(name = "price_cents", nullable = false)
    private int priceCents;          // store money as integer cents

    // @Transient: computed in memory only — NOT a database column.
    @Transient
    private String displayPrice;

    protected Product() { }

    public Product(String name, int priceCents) {
        this.name = name;
        this.priceCents = priceCents;
    }

    // derived value, recomputed after load — never stored
    public String getDisplayPrice() {
        return "$" + (priceCents / 100) + "." + String.format("%02d", priceCents % 100);
    }
}

🧠 Quick Recall

Predict before you peek.

Answer: Invoice — JPA derives the table name from the entity name unless @Table(name = "...") overrides it.

Answer: null for a generated Long id — the database assigns it on insert.

Answer: @Transient. The field stays in memory only and is never mapped to a column.

🎯 YOUR TURN — Map a Book

Create a Book entity with a generated id, a NOT NULL title, and a unique isbn_13 column.

import jakarta.persistence.*;

// YOUR TURN: map a Book entity.
//  - table name "books"
//  - id: Long, auto-generated by the database
//  - title: String, NOT NULL, max length 200
//  - isbn:  String, mapped to column "isbn_13", UNIQUE
//  - add a no-arg constructor and a (title, isbn) constructor
//
// @Entity
// @Table(name = "books")
// public class Book { ... }

🧩 MINI-CHALLENGE — Employee with a derived field

Map an Employee with custom column names and a @Transient computed salary.

import jakarta.persistence.*;

// MINI-CHALLENGE: map an Employee with a couple of subtleties.
//  - table "employees"
//  - id auto-generated
//  - fullName -> column "full_name", NOT NULL, length 120
//  - salaryCents (int) -> column "salary_cents", NOT NULL
//  - a @Transient field "annualSalary" computed from salaryCents (NOT stored)
//
// Think: which annotation keeps annualSalary out of the table?
//        which strategy lets the database own the id?

Common Errors

Pro Tips

📋 Quick Reference

AnnotationPurposeNote
@Entityclass is persistentMaps to a table
@Table(name)custom table nameDefaults to class name
@Idprimary keyExactly one
@GeneratedValueauto-generate keyIDENTITY / SEQUENCE
@Column(name)custom column nameDefaults to field
@Column(nullable)NOT NULL constraintfalse = required
@Column(length)VARCHAR sizeFor String columns
@Column(unique)UNIQUE constraintNo duplicates
@Transientnot persistedIn-memory only
no-arg ctorrequired by JPAprotected is fine

📚 Resources / Keep Learning

🎉 Lesson Complete!

You can now map a class to a table with @Entity, define and auto-generate a primary key with @Id and @GeneratedValue, shape columns and tables with @Column and @Table, and exclude computed fields with @Transient.

Practice quiz

What does the @Entity annotation mark a class as?

  • A REST controller
  • A JPA-managed persistent entity mapped to a table
  • A Spring bean only
  • A configuration file

Answer: A JPA-managed persistent entity mapped to a table. @Entity tells JPA the class maps to a database table and its instances are persistent entities.

Which annotation marks the primary key field of an entity?

  • @Key
  • @Primary
  • @Id
  • @Column(primary=true)

Answer: @Id. @Id designates the field that maps to the table's primary key.

What does @GeneratedValue(strategy = GenerationType.IDENTITY) do?

  • Generates a random UUID in Java
  • Lets the database auto-increment the key column
  • Disables key generation
  • Uses a table to store keys only

Answer: Lets the database auto-increment the key column. IDENTITY delegates key generation to the database's own auto-increment column.

By default, what table name does JPA use for an @Entity class named Customer?

  • customers
  • tbl_Customer
  • Customer
  • entity_customer

Answer: Customer. Without @Table, JPA derives the table name from the entity name, here Customer.

Which annotation overrides the default table name for an entity?

  • @Table(name = "...")
  • @Entity(table = "...")
  • @MapTo
  • @Schema

Answer: @Table(name = "..."). @Table(name = "...") sets an explicit table name, useful when it differs from the class name.

What does @Column(nullable = false) enforce?

  • The column is the primary key
  • The column is unique
  • The column is indexed
  • The column may not store NULL

Answer: The column may not store NULL. nullable = false adds a NOT NULL constraint to the generated column.

A no-argument constructor is required on a JPA entity because...

  • Java forbids other constructors
  • The persistence provider instantiates entities reflectively
  • It speeds up queries
  • It is needed for equals()

Answer: The persistence provider instantiates entities reflectively. JPA providers create entity instances via a no-arg constructor before populating fields.

What is the effect of @Column(length = 100) on a String field?

  • Limits the Java String length at runtime
  • Truncates reads to 100 chars
  • Sets the VARCHAR length in the generated DDL
  • Indexes the first 100 characters

Answer: Sets the VARCHAR length in the generated DDL. length controls the column size (e.g. VARCHAR(100)) in the schema DDL.

If you omit @Column on a mapped field, JPA will...

  • Skip the field entirely
  • Throw an exception at startup
  • Map it using the field name and default settings
  • Treat it as transient

Answer: Map it using the field name and default settings. Persistent fields are mapped automatically; @Column is only needed to customize the mapping.

Which annotation tells JPA NOT to persist a particular field?

  • @Transient
  • @Ignore
  • @Skip
  • @NotPersisted

Answer: @Transient. @Transient marks a field as non-persistent so JPA leaves it out of the table mapping.

Continue this course

Frequently asked questions

What does the @Entity annotation actually do?

@Entity marks a plain Java class as a JPA entity — an object whose state is mapped to a row in a relational table. The persistence provider (commonly Hibernate under Spring Data JPA) scans for @Entity classes, builds a mapping between fields and columns, and can then store, load, update, and delete instances as database rows. Without @Entity, the class is just a regular object the persistence layer ignores.

Why does a JPA entity need a no-argument constructor?

The persistence provider instantiates entities reflectively when it materializes rows from the database — it calls a no-arg constructor and then populates fields. JPA requires this constructor to be present and at least protected (or public). If you only define a constructor with arguments, the provider cannot create instances and startup or query execution fails. The constructor can be protected so you do not encourage misuse from your own code.

What is the difference between @Id and @GeneratedValue?

@Id marks which field is the entity's primary key — the unique identifier for each row. @GeneratedValue says the value of that key is produced automatically rather than set by your code. The strategy attribute chooses how: GenerationType.IDENTITY uses a database auto-increment column, SEQUENCE uses a database sequence, AUTO lets the provider pick, and TABLE uses a separate table to track values. You use @Id alone for assigned keys, and add @GeneratedValue when the database or provider should generate them.

When do I actually need @Column and @Table?

You only need them to override defaults. JPA derives the table name from the entity name and each column name from the field name, mapping every persistent field automatically. Add @Table(name = "...") when the table name differs from the class, and @Column when you want a different column name, a length, a NOT NULL constraint (nullable = false), uniqueness (unique = true), or other column-level settings. If the defaults are fine, you can leave both off.

How do I stop a field from being saved to the database?

Annotate it with @Transient. JPA persists every non-static, non-transient field by default, so a value you compute in memory — like a formatted display price derived from a stored integer — would otherwise become a column. @Transient tells the provider to skip the field entirely: it lives only on the in-memory object and is recomputed as needed, never written to or read from the table.

Related lessons