Cheat SheetsHibernate & JPAFundamentals

Fundamentals — Cheat Sheet

Hibernate & JPA · 7 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Fundamentals
Hibernate & JPA7 topicsQuick revision reference
1

JPA vs Hibernate

JPA is the Java specification (API + annotations); Hibernate is the most popular implementation. You code against JPA interfaces for portability but benefit from Hibernate-specific features.

  • JPA is the specification (annotations, EntityManager, JPQL); Hibernate is the implementation.
  • Code to JPA interfaces for portability; use Hibernate-specific features only when needed.
  • Spring Data JPA sits above JPA — JpaRepository generates queries using Hibernate under the hood.
  • @Entity, @Id, @ManyToOne, @Query — all JPA standard; @NaturalId, @Filter, @Cache — Hibernate-specific.
  • The full stack: Spring Data JPA → JPA (EntityManager) → Hibernate → HikariCP → DB.
  • spring.jpa.show-sql=true and format_sql=true are invaluable for debugging generated SQL.
Java — JPA standard vs Hibernate-specific annotations
// JPA-standard — works on any provider
import jakarta.persistence.*;

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;
}

// JPA standard EntityManager
@Repository
public class ProductJpaRepository {
    @PersistenceContext
    private EntityManager em;

    public Optional<Product> findById(Long id) {
        return Optional.ofNullable(em.find(Product.class, id));
    }
}

// Hibernate-specific — tied to Hibernate
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.BatchSize;

@Entity
public class Product {
    @NaturalId                    // Hibernate-specific: cache by natural key
    private String sku;

    @BatchSize(size = 25)         // Hibernate-specific: IN-clause batch loading
    @OneToMany(mappedBy = "product")
    private List<Review> reviews;
}
2

EntityManager & Persistence Context

The EntityManager is the gateway to JPA operations; the persistence context is its first-level cache holding managed entity instances for the duration of a transaction.

  • EntityManager manages the Persistence Context — a first-level (L1) cache per transaction.
  • Entity states: Transient → Managed → Detached (or Removed).
  • Dirty checking: Hibernate compares entity snapshots at flush time and generates UPDATE if changed.
  • em.find() with the same ID in the same transaction returns the same Java object (cache hit).
  • JPQL queries bypass L1 cache but results are merged into the persistence context.
  • FlushModeType.AUTO flushes before queries; COMMIT defers to transaction commit.
Java — entity lifecycle states
@Service
@Transactional
public class OrderService {

    @PersistenceContext
    private EntityManager em;

    public void demonstrateLifecycle() {

        // 1. TRANSIENT — new object, not yet associated with persistence context
        Order order = new Order(customerId, items);   // transient

        // 2. MANAGED — persisted; tracked by persistence context
        em.persist(order);                             // managed (INSERT on flush)
        // order.getId() is now populated (IDENTITY strategy)

        // 3. Dirty checking — change detected automatically
        order.setStatus(OrderStatus.PLACED);           // no explicit update needed
        // Hibernate compares snapshot vs current state on flush → generates UPDATE

        // 4. DETACHED — entity no longer tracked
        em.detach(order);
        order.setStatus(OrderStatus.SHIPPED);          // change NOT persisted

        // 5. Re-attach with merge
        Order managed = em.merge(order);               // SELECT + UPDATE
        managed.setStatus(OrderStatus.DELIVERED);      // this change IS persisted

        // 6. REMOVED — marked for deletion
        em.remove(managed);                            // DELETE on flush
    }
}
3

Entity Lifecycle States

An entity moves through four states: transient (new, no ID), managed (tracked by context), detached (disconnected after context closes), and removed (scheduled for DELETE).

  • Four states: Transient (new, no context), Managed (tracked, changes auto-flushed), Detached (was managed, not tracked), Removed (DELETE pending).
  • Dirty checking: Hibernate compares managed entity fields to a snapshot at flush time — changed fields generate UPDATE automatically.
  • merge() returns a new managed reference; the detached instance passed in stays detached — always use the returned value.
  • You cannot remove a detached entity — call merge() first to re-attach it, then remove() on the managed copy.
  • @Transactional(readOnly=true) skips dirty checking and snapshot creation — use it for all read-only service methods for better performance.
  • LazyInitializationException occurs when a LAZY proxy is accessed after the persistence context (transaction) has closed.
Java — Entity Lifecycle States
@Service
@Transactional
public class OrderService {

    @PersistenceContext
    private EntityManager em;

    public void demo() {
        // 1. Transient — new object, no persistence context
        Order order = new Order();    // state: TRANSIENT
        order.setReference("ORD-1");

        // 2. Managed — persist transitions to MANAGED
        em.persist(order);            // state: MANAGED; INSERT on flush
        order.setStatus("PENDING");   // dirty check — UPDATE on flush (auto!)

        // 3. Detached — clear all entities from context
        em.flush();                   // SQL executed now
        em.detach(order);             // state: DETACHED — changes no longer tracked
        order.setStatus("SHIPPED");   // NOT tracked — no SQL

        // 4. Re-attach — merge returns a MANAGED copy
        Order managed = em.merge(order); // state: MANAGED (new reference!)
        // The original 'order' is still DETACHED; use 'managed' going forward

        // 5. Removed — schedule DELETE
        em.remove(managed);            // state: REMOVED; DELETE on flush
    }
}
4

@Entity, @Table, @Id

@Entity marks a class as a JPA-managed entity; @Table customises the table name and schema; @Id designates the primary key field; every entity must have exactly one @Id.

  • @Entity is required; @Table is optional — use it to customise table name or add constraints.
  • Every entity must have exactly one @Id field and a no-arg constructor.
  • Entities must not be final — Hibernate needs to subclass them for lazy proxying.
  • @IdClass and @EmbeddedId support composite primary keys.
  • Implement equals/hashCode based on a business key (natural ID), not the generated DB id.
  • Define unique constraints and indexes via @Table annotations for JPA-managed schema generation.
Java — @Entity, @Table with constraints and indexes
// Basic entity — table name defaults to "Product" (case-insensitive)
@Entity
public class Product {
    @Id
    private Long id;
    private String name;
}

// Custom table name, schema, and unique constraint
@Entity
@Table(
    name    = "tbl_products",
    schema  = "shop",
    uniqueConstraints = {
        @UniqueConstraint(name = "uq_product_sku", columnNames = {"sku"}),
        @UniqueConstraint(name = "uq_product_barcode", columnNames = {"barcode_type", "barcode_value"})
    },
    indexes = {
        @Index(name = "idx_product_category", columnList = "category_id"),
        @Index(name = "idx_product_name",     columnList = "name")
    }
)
public class Product {
    @Id
    private Long id;

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

    @Column(name = "barcode_type", length = 10)
    private String barcodeType;

    @Column(name = "barcode_value", length = 50)
    private String barcodeValue;

    // JPA requires a no-arg constructor (public or protected)
    protected Product() {}
    public Product(Long id, String sku) { this.id = id; this.sku = sku; }
}
5

@GeneratedValue Strategies

AUTO delegates to the provider; IDENTITY uses DB auto-increment; SEQUENCE uses a DB sequence (most efficient for batch inserts); TABLE uses a dedicated ID table (avoid in production).

  • IDENTITY uses DB auto-increment — simple but disables JDBC batch INSERT.
  • SEQUENCE pre-fetches ID blocks (allocationSize) — enables batching; use for bulk inserts.
  • TABLE strategy is portable but uses DB row locks — avoid in production.
  • UUID (@GeneratedValue(strategy=UUID)) is useful for distributed systems with no sequence.
  • UUIDv7 (time-ordered) avoids random B-tree index splits unlike random UUIDv4.
  • For bulk inserts: flush + clear every N entities to avoid OutOfMemoryError from L1 cache growth.
Java — IDENTITY vs SEQUENCE strategies
// IDENTITY — simple, works with MySQL AUTO_INCREMENT
// ✗ Disables JDBC batch inserts (Hibernate needs each insert's ID immediately)
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

// SEQUENCE — pre-fetches ID blocks; enables batch inserts
// Works with PostgreSQL natively; MySQL needs a workaround sequence table
@Entity
@SequenceGenerator(
    name            = "order_seq",
    sequenceName    = "order_id_seq",   // DB sequence name
    allocationSize  = 50                // pre-allocate 50 IDs per round-trip
)
public class Order {
    @Id
    @GeneratedValue(
        strategy  = GenerationType.SEQUENCE,
        generator = "order_seq"
    )
    private Long id;
}

// With allocationSize=50:
// 1st ID fetch: SELECT nextval('order_id_seq') → 1
// Hibernate caches IDs 1-50 in memory — no DB call for the next 49 entities
// 51st entity: SELECT nextval → 51, caches 51-100, etc.
6

@Column Mapping

@Column maps a field to a specific column with custom name, length, precision, nullable, and unique constraints that are honoured during schema generation and validation.

  • @Column without attributes uses field name (with configured naming strategy) as column name
  • nullable=false generates NOT NULL in DDL and is checked during Hibernate validation
  • Use precision and scale for DECIMAL columns — never map currency to a FLOAT
  • insertable=false / updatable=false exclude the column from INSERT / UPDATE SQL
  • columnDefinition provides raw DDL — useful for DB-specific types (TEXT, JSON, JSONB)
  • spring.jpa.hibernate.ddl-auto=validate checks your mappings against the actual schema at startup
Java — @Column attribute examples
@Entity
@Table(name = "products")
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, precision = 10, scale = 2)
    private BigDecimal price;               // maps to DECIMAL(10, 2)

    @Column(name = "sku_code", unique = true, length = 50)
    private String sku;

    @Column(columnDefinition = "TEXT")      // raw DDL type override
    private String description;

    @Column(updatable = false)              // set on insert, never updated
    private LocalDateTime createdAt;
}
7

@Embeddable & @Embedded

@Embeddable defines a reusable value-type POJO; @Embedded includes its columns in the owning entity table. Use for Address, Money, or other value objects with no identity.

  • @Embeddable class columns are stored in the owning entity's table — no separate table or join
  • Value types have no identity; use value equality (equals/hashCode on all fields)
  • @AttributeOverrides is required when embedding the same type more than once in one entity
  • A null @Embedded field stores all its columns as NULL; all-NULL columns load back as null
  • @Embeddable classes need a no-arg constructor (can be protected/package-private)
  • Embeddables can nest other embeddables — keep nesting shallow for readability
Java — @Embeddable Address value object
@Embeddable
public class Address {
    @Column(nullable = false, length = 200)
    private String street;

    @Column(nullable = false, length = 100)
    private String city;

    @Column(nullable = false, length = 10)
    private String postcode;

    @Column(nullable = false, length = 2)
    private String countryCode;

    // no-arg constructor required
    protected Address() {}

    public Address(String street, String city, String postcode, String countryCode) {
        this.street = street; this.city = city;
        this.postcode = postcode; this.countryCode = countryCode;
    }

    // equals() and hashCode() based on all fields (value equality)
}

@Entity
public class Customer {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded                           // columns: street, city, postcode, country_code
    private Address billingAddress;
}
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/hibernate