Toolmingo
Guides24 min read

50 Hibernate Interview Questions (With Answers)

Top Hibernate ORM interview questions with clear answers and code examples — covering entity lifecycle, mappings, N+1 problem, caching, HQL, transactions, and Spring integration.

Hibernate interviews test your understanding of ORM fundamentals, entity lifecycle management, relationship mappings, performance pitfalls (especially N+1), caching layers, and Spring Data JPA integration. This guide covers the 50 most common questions — with concise answers and runnable code examples.

Quick reference

Topic Most asked questions
ORM & Architecture What is Hibernate, SessionFactory vs EntityManager
Entity Lifecycle Transient / Persistent / Detached / Removed states
Mappings @OneToMany, @ManyToMany, cascade types, fetch types
N+1 Problem Lazy loading, JOIN FETCH, @BatchSize, @EntityGraph
Caching L1 (Session), L2 (EhCache/Redis), Query Cache
HQL/JPQL Named queries, Criteria API, native SQL
Transactions & Locking @Transactional, optimistic vs pessimistic locking
Inheritance SINGLE_TABLE, TABLE_PER_CLASS, JOINED strategies

ORM & Architecture

1. What is Hibernate and why use an ORM?

Hibernate is a Java ORM (Object-Relational Mapping) framework that maps Java objects to relational database tables, eliminating most JDBC boilerplate.

Without Hibernate With Hibernate
Manual SQL for every operation Auto-generated SQL
Manual ResultSet mapping Automatic entity population
No caching L1 + optional L2 cache
No relationship management Cascade, lazy loading built-in
Schema per-DB Dialect abstraction (MySQL, PG, Oracle)
Verbose connection handling Connection pooling integrated

Hibernate implements the JPA (Jakarta Persistence API) specification, so code written against JPA interfaces is portable across providers (EclipseLink, OpenJPA).


2. What is the difference between Hibernate and JPA?

Aspect JPA Hibernate
Type Specification (interfaces only) Implementation of JPA + extensions
Package jakarta.persistence.* org.hibernate.*
Portability Switch providers without code change Vendor-specific features (e.g., @Formula)
Configuration persistence.xml hibernate.cfg.xml or Spring auto-config
EntityManager JPA standard Hibernate Session extends it

Best practice: write against JPA APIs, use Hibernate-specific features only when needed.


3. What is SessionFactory and how does it differ from Session?

// SessionFactory — heavyweight, one per application (thread-safe)
SessionFactory sf = new Configuration()
    .configure()
    .buildSessionFactory();

// Session — lightweight, one per request/transaction (NOT thread-safe)
try (Session session = sf.openSession()) {
    Transaction tx = session.beginTransaction();
    session.persist(new Product("Laptop", 999.99));
    tx.commit();
}
Aspect SessionFactory Session
Weight Heavyweight (builds schema, prepares SQL) Lightweight wrapper
Thread safety Thread-safe NOT thread-safe
Lifecycle Application scope Request / transaction scope
L2 cache Houses the L2 cache Uses L1 cache per instance
JPA equivalent EntityManagerFactory EntityManager

4. What is the difference between Session and EntityManager?

Session is the Hibernate-native API; EntityManager is the JPA-standard API. Session extends EntityManager — all JPA methods are available on Session plus Hibernate-specific extras.

Operation JPA (EntityManager) Hibernate (Session)
Save new persist() persist() / save()
Load by PK find() get() / load()
Update detached merge() merge() / update()
Delete remove() remove() / delete()
Flush flush() flush()
HQL query createQuery() (JPQL) createQuery() (HQL superset)
Native SQL createNativeQuery() createNativeQuery()

5. What are the core Hibernate components?

Application
    │
    ├─ Configuration ──► SessionFactory (one per DB)
    │                         │
    │                    Session (per request)
    │                         │
    │              ┌──────────┴──────────┐
    │         Transaction            Query / Criteria
    │              │
    │         JDBC Connection Pool (HikariCP)
    │              │
    └──────────────►  Database
  • Configuration — reads hibernate.cfg.xml / Spring properties, registers entity classes
  • SessionFactory — compiled metadata, L2 cache home, connection pool
  • Session — unit of work, L1 cache, dirty checking
  • Transaction — ACID boundary
  • Query / Criteria API — execute HQL, JPQL, native SQL

Entity Lifecycle

6. What are the four entity lifecycle states?

new Product()          → Transient   (no PK, not tracked)
session.persist(p)     → Persistent  (tracked, in L1 cache)
session.detach(p)      → Detached    (not tracked, has PK)
session.remove(p)      → Removed     (scheduled for DELETE)
State Has PK? Tracked by Session? DB row?
Transient No No No
Persistent Yes Yes Yes (or pending INSERT)
Detached Yes No Yes
Removed Yes Yes (for deletion) Pending DELETE

7. What is dirty checking in Hibernate?

When a Session flushes, Hibernate compares the current state of each persistent entity against its snapshot captured at load time. If different, it auto-generates an UPDATE — without you calling any save method.

try (Session s = sf.openSession()) {
    Transaction tx = s.beginTransaction();
    Product p = s.get(Product.class, 1L); // snapshot taken
    p.setPrice(799.99);                   // no explicit save needed
    tx.commit();                          // Hibernate detects change → UPDATE
}

Dirty checking only applies to persistent entities (not detached).


8. What is the difference between save(), persist(), update(), merge(), and saveOrUpdate()?

Method Input state Returns DB immediately? JPA standard?
persist() Transient void On flush
save() Transient Serializable PK On flush (INSERT may fire now for SEQUENCE) ✗ (Hibernate)
update() Detached void On flush
merge() Any New managed copy On flush
saveOrUpdate() Transient/Detached void On flush

Prefer persist() and merge() — they're JPA-standard and portable.


9. What is the difference between get() and load()?

// get() — hits DB immediately, returns null if not found
Product p1 = session.get(Product.class, 99L);   // DB hit now
if (p1 == null) System.out.println("Not found");

// load() — returns a proxy, DB hit deferred until first field access
Product p2 = session.load(Product.class, 99L);   // no DB hit yet
System.out.println(p2.getName());                 // DB hit here
// Throws ObjectNotFoundException if row doesn't exist
Aspect get() load()
DB hit Immediate Lazy (on first access)
Not found Returns null Throws ObjectNotFoundException
Return type Real entity Proxy object
Use when Existence uncertain Existence guaranteed (e.g., FK reference)

10. What happens to detached entities?

A detached entity has a PK but is no longer tracked by any Session. Changes to it are not auto-saved.

Product p;
try (Session s = sf.openSession()) {
    p = s.get(Product.class, 1L);
} // Session closes → p becomes Detached

// p.setPrice(500) changes are ignored — no active session

// Re-attach options:
try (Session s2 = sf.openSession()) {
    Product managed = s2.merge(p); // returns new managed instance
    // OR
    s2.update(p);                  // re-attaches p itself (Hibernate-only)
}

Relationship Mappings

11. What are the Hibernate relationship mapping types?

Annotation Meaning Example
@OneToOne 1:1 User ↔ UserProfile
@OneToMany 1:N Order → [OrderItem]
@ManyToOne N:1 OrderItem → Order
@ManyToMany M:N Student ↔ [Course]

12. How do you map a @OneToMany relationship?

@Entity
public class Order {
    @Id @GeneratedValue Long id;

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

@Entity
public class OrderItem {
    @Id @GeneratedValue Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    private String product;
    private int quantity;
}

Key points:

  • mappedBy on the non-owning side (parent) — no extra join table column
  • Owning side = the side with @JoinColumn (the FK column)
  • orphanRemoval = true — deletes OrderItem when removed from the list

13. What are the JPA cascade types?

CascadeType Effect
PERSIST persist(parent) also persists children
MERGE merge(parent) also merges children
REMOVE remove(parent) also removes children
REFRESH refresh(parent) also refreshes children
DETACH detach(parent) also detaches children
ALL All of the above
// CascadeType.PERSIST: save order + items in one call
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items;

⚠️ Avoid CascadeType.REMOVE on @ManyToMany — it will delete the other side's rows.


14. What is the difference between FetchType.LAZY and FetchType.EAGER?

Aspect LAZY EAGER
Load timing On first access With parent (JOIN or extra SELECT)
Default for @OneToMany / @ManyToMany LAZY ✓
Default for @ManyToOne / @OneToOne EAGER (JPA default)
Risk LazyInitializationException outside Session N+1 / cartesian product
Recommendation Prefer LAZY everywhere Use only for tiny, always-needed associations
// EAGER causes extra JOIN even when you don't need address
@ManyToOne(fetch = FetchType.LAZY)   // override the EAGER default
@JoinColumn(name = "address_id")
private Address address;

15. How do you map a @ManyToMany relationship?

@Entity
public class Student {
    @Id @GeneratedValue Long id;
    String name;

    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();
}

@Entity
public class Course {
    @Id @GeneratedValue Long id;
    String title;

    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();
}

Use a Set (not List) to avoid duplicate rows in the join table.


The N+1 Problem

16. What is the N+1 problem and why is it dangerous?

The N+1 problem: loading 1 parent fires N additional queries for each child collection.

// BAD: N+1
List<Order> orders = session.createQuery("FROM Order", Order.class).list();
// 1 query: SELECT * FROM orders (returns 100 rows)
for (Order o : orders) {
    System.out.println(o.getItems().size());
    // 100 queries: SELECT * FROM order_item WHERE order_id = ?
}
// Total: 101 queries!

On a table with 10,000 orders this becomes 10,001 queries — catastrophic performance.


17. How do you fix the N+1 problem?

Option 1: JOIN FETCH (JPQL)

List<Order> orders = session.createQuery(
    "SELECT DISTINCT o FROM Order o JOIN FETCH o.items", Order.class
).list();
// 1 query with JOIN — loads orders + items together

Option 2: @EntityGraph

@NamedEntityGraph(name = "Order.withItems",
    attributeNodes = @NamedAttributeNode("items"))

// Spring Data JPA usage:
@EntityGraph("Order.withItems")
List<Order> findAll();

Option 3: @BatchSize

@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
@BatchSize(size = 50)
private List<OrderItem> items;
// Loads items for 50 orders in one IN-clause query
Solution SQL generated Best for
JOIN FETCH Single JOIN Small-to-medium result sets
@EntityGraph Single JOIN Spring Data JPA repositories
@BatchSize Batched IN queries Large result sets, multiple associations
Subselect One subquery per collection Read-only, many parents

18. What is @Fetch(FetchMode.SUBSELECT)?

@OneToMany(mappedBy = "order")
@Fetch(FetchMode.SUBSELECT)
private List<OrderItem> items;

// Generates:
// 1. SELECT * FROM orders WHERE ...
// 2. SELECT * FROM order_item WHERE order_id IN (SELECT id FROM orders WHERE ...)

Good for collections where the parent query is complex; avoids a JOIN on the main query.


Caching

19. What are the Hibernate cache levels?

Level Scope Always enabled? What it caches
L1 (Session cache) Single Session Yes Entities loaded in this session
L2 cache SessionFactory (shared) No (opt-in) Entities across sessions
Query cache SessionFactory No (opt-in) Query results (PKs)

20. How does the L1 (first-level) cache work?

try (Session s = sf.openSession()) {
    Product p1 = s.get(Product.class, 1L); // DB hit → cached in Session
    Product p2 = s.get(Product.class, 1L); // returns from L1 cache — NO DB hit
    System.out.println(p1 == p2);          // true — same object reference
}
// L1 cache is cleared when Session closes

The L1 cache is always on and cannot be disabled. Call session.evict(entity) to remove one entity or session.clear() to clear all.


21. How do you enable and use the L2 (second-level) cache?

<!-- hibernate.cfg.xml -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">
    org.hibernate.cache.jcache.JCacheRegionFactory
</property>
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
    @Id Long id;
    String name;
    // ...
}
CacheConcurrencyStrategy Read-only? Write-safe? Use when
READ_ONLY Immutable reference data
NONSTRICT_READ_WRITE Eventual Low-write, some staleness OK
READ_WRITE Soft lock Frequently updated entities
TRANSACTIONAL XA transaction High consistency required

22. What is the Query Cache?

The query cache stores the list of entity IDs (not full entities) returned by a specific HQL/JPQL query. The actual entities still come from L1/L2 cache or DB.

// Enable globally
hibernate.cache.use_query_cache=true

// Per query
List<Product> cheapProducts = session.createQuery(
    "FROM Product WHERE price < :maxPrice", Product.class)
    .setParameter("maxPrice", 100.0)
    .setCacheable(true)          // opt into query cache
    .setCacheRegion("cheapProducts")
    .list();

⚠️ Only useful for queries with stable results and cacheable entities. Invalidated whenever any entity in the result table changes.


HQL, JPQL & Criteria API

23. What is HQL and how does it differ from SQL?

HQL (Hibernate Query Language) is object-oriented — it references entity class names and field names, not table/column names. Hibernate translates it to SQL.

// HQL — entity name "Product", field name "price"
List<Product> results = session.createQuery(
    "FROM Product p WHERE p.price > :minPrice ORDER BY p.name", Product.class)
    .setParameter("minPrice", 50.0)
    .list();

// Generated SQL (approximate):
// SELECT * FROM product WHERE price > 50.0 ORDER BY name
Aspect SQL HQL/JPQL
References Table names, column names Entity class names, field names
DB portability DB-specific syntax DB-agnostic (Hibernate translates)
SELECT * Valid Not needed — FROM Product returns full entities
Joins On FK columns On mapped associations: JOIN p.category
UPDATE/DELETE Supported Supported (bulk operations)

24. What are Named Queries?

@Entity
@NamedQuery(
    name = "Product.findByCategory",
    query = "SELECT p FROM Product p WHERE p.category.name = :catName"
)
public class Product { ... }

// Usage
List<Product> electronics = session.createNamedQuery(
    "Product.findByCategory", Product.class)
    .setParameter("catName", "Electronics")
    .list();

Named queries are compiled at startup — syntax errors surface immediately, not at runtime.


25. How do you use the Criteria API?

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Product> cq = cb.createQuery(Product.class);
Root<Product> root = cq.from(Product.class);

cq.select(root)
  .where(
      cb.and(
          cb.greaterThan(root.get("price"), 100.0),
          cb.like(root.get("name"), "%Laptop%")
      )
  )
  .orderBy(cb.asc(root.get("price")));

List<Product> products = session.createQuery(cq).getResultList();
Approach Type-safe? Dynamic queries? Verbose?
HQL/JPQL (String) Awkward string concat Low
Criteria API ✓ (Metamodel) Excellent High
Named queries No Low
Native SQL Possible Medium

26. How do you execute a native SQL query?

// Returns mapped entities
List<Product> products = session.createNativeQuery(
    "SELECT * FROM product WHERE price > 100 LIMIT 10",
    Product.class
).getResultList();

// Returns raw Object[]
List<Object[]> rows = session.createNativeQuery(
    "SELECT p.name, c.name FROM product p JOIN category c ON p.category_id = c.id"
).getResultList();

Use native SQL when HQL can't express a DB-specific feature (e.g., LATERAL JOIN, MATCH AGAINST).


Inheritance Strategies

27. What are the Hibernate inheritance mapping strategies?

Strategy Tables Pros Cons
SINGLE_TABLE 1 table for all subclasses Best query performance, simple Nullable columns for subclass fields; wide table
TABLE_PER_CLASS 1 table per concrete class No joins needed Polymorphic queries use UNION; no shared PK sequence
JOINED 1 base table + 1 per subclass Normalized; no nulls JOIN required for every subclass query

28. How do you implement SINGLE_TABLE inheritance?

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "vehicle_type")
public class Vehicle {
    @Id @GeneratedValue Long id;
    String brand;
}

@Entity
@DiscriminatorValue("CAR")
public class Car extends Vehicle {
    int doors;
}

@Entity
@DiscriminatorValue("TRUCK")
public class Truck extends Vehicle {
    double payloadTons;
}
// One table: vehicle(id, brand, vehicle_type, doors, payload_tons)
// doors and payload_tons are nullable for the other subclass

29. How do you implement JOINED inheritance?

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Payment {
    @Id @GeneratedValue Long id;
    double amount;
}

@Entity
@PrimaryKeyJoinColumn(name = "payment_id")
public class CreditCardPayment extends Payment {
    String cardNumber;
}
// Tables: payment(id, amount) + credit_card_payment(payment_id, card_number)
// Query: SELECT p.*, c.* FROM payment p JOIN credit_card_payment c ON ...

Transactions & Locking

30. What is the difference between optimistic and pessimistic locking?

Aspect Optimistic Pessimistic
Mechanism @Version field — detect conflict at commit DB-level lock (SELECT FOR UPDATE)
Concurrency High Low (blocks other readers/writers)
Conflict handling OptimisticLockException on commit Blocked until lock released
Best for Read-heavy; low conflict probability Write-heavy; data integrity critical

31. How do you implement optimistic locking?

@Entity
public class Account {
    @Id Long id;
    double balance;

    @Version
    int version;  // Hibernate auto-increments on every UPDATE
}

// Thread A reads account at version 5
// Thread B reads account at version 5

// Thread B commits → version becomes 6
// Thread A tries to commit → WHERE id=? AND version=5 matches 0 rows
// → throws OptimisticLockException

32. How do you implement pessimistic locking?

// Lock for the duration of the transaction
Account account = session.get(Account.class, 1L, LockMode.PESSIMISTIC_WRITE);
// Generates: SELECT ... FROM account WHERE id = 1 FOR UPDATE

account.setBalance(account.getBalance() - 100);
// Other sessions trying to read/write this row will block until commit
LockMode SQL generated Effect
PESSIMISTIC_READ LOCK IN SHARE MODE Others can read, not write
PESSIMISTIC_WRITE FOR UPDATE Exclusive lock
PESSIMISTIC_FORCE_INCREMENT FOR UPDATE + version++ Write lock + optimistic version bump

33. What are the @Transactional propagation types in Spring?

Propagation Behavior
REQUIRED (default) Join existing tx; create new if none
REQUIRES_NEW Always create new tx; suspend existing
SUPPORTS Join if exists; run non-tx if none
NOT_SUPPORTED Run non-tx; suspend existing tx
MANDATORY Must have existing tx; throws if none
NEVER Must NOT have tx; throws if one exists
NESTED Nested tx (savepoint) inside existing tx
@Service
public class OrderService {
    @Transactional  // REQUIRED — joins caller's tx
    public void placeOrder(Order order) { ... }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void auditLog(String event) { ... }  // always own tx
}

34. What are @Transactional isolation levels?

Isolation level Dirty read Non-repeatable read Phantom read
READ_UNCOMMITTED ✓ possible ✓ possible ✓ possible
READ_COMMITTED (PG default) ✓ possible ✓ possible
REPEATABLE_READ (MySQL default) ✓ possible
SERIALIZABLE
@Transactional(isolation = Isolation.REPEATABLE_READ)
public Product getForUpdate(Long id) { ... }

Entity Mappings & Schema

35. How do you map an entity to a table?

@Entity
@Table(name = "tbl_product",
       uniqueConstraints = @UniqueConstraint(columnNames = {"sku"}),
       indexes = @Index(columnList = "category_id"))
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

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

    @Enumerated(EnumType.STRING)   // store enum as VARCHAR
    private ProductStatus status;

    @Temporal(TemporalType.TIMESTAMP)  // for legacy java.util.Date
    private Date createdAt;
}

36. What are the @GeneratedValue strategy options?

Strategy DB behavior Best for
IDENTITY Auto-increment column MySQL, SQL Server
SEQUENCE DB sequence object PostgreSQL, Oracle (best performance)
TABLE Dedicated PK table Any DB (worst performance)
AUTO Let Hibernate choose Portability (not recommended in prod)
UUID Hibernate generates UUID Distributed systems
// PostgreSQL — best approach
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_id_seq",
                   allocationSize = 50)
private Long id;

allocationSize = 50 — Hibernate fetches 50 IDs at once, reducing DB roundtrips.


37. How do you use @Embedded and @Embeddable?

@Embeddable
public class Address {
    private String street;
    private String city;
    private String zipCode;
}

@Entity
public class Customer {
    @Id Long id;
    String name;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "billing_street"))
    })
    private Address billingAddress;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "street", column = @Column(name = "shipping_street"))
    })
    private Address shippingAddress;
}
// All fields flattened into customer table: id, name, billing_street, billing_city, ...

38. What is orphanRemoval and when should you use it?

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

// Usage:
Order order = session.get(Order.class, 1L);
order.getItems().remove(0);   // with orphanRemoval: fires DELETE for that item
                               // without orphanRemoval: item row stays (orphan)

Use orphanRemoval = true when child entities have no meaning outside the parent. Don't use it for shared entities (e.g., Category shared by many Products).


Performance & Best Practices

39. What is the open-session-in-view anti-pattern?

spring.jpa.open-in-view=true (Spring Boot default) keeps the Hibernate Session open during view rendering, allowing lazy-load proxies to be initialized in templates/controllers.

Problems:

  • DB connection held for the full HTTP request duration
  • Hidden N+1 queries triggered in the view layer
  • Hard to detect in tests

Fix:

spring.jpa.open-in-view=false

Load all required data in the service layer before returning to the controller.


40. How do you do bulk updates/deletes efficiently?

// BAD: loads all entities into memory, updates one by one
List<Product> discontinued = session.createQuery(
    "FROM Product WHERE status = 'DISCONTINUED'", Product.class).list();
discontinued.forEach(p -> p.setPrice(0));
// N UPDATE statements

// GOOD: bulk update — single SQL statement
int updated = session.createQuery(
    "UPDATE Product SET price = 0 WHERE status = :status")
    .setParameter("status", "DISCONTINUED")
    .executeUpdate();
// 1 UPDATE statement

// GOOD: bulk delete
int deleted = session.createQuery(
    "DELETE FROM Product WHERE status = :status")
    .setParameter("status", "DISCONTINUED")
    .executeUpdate();

⚠️ Bulk operations bypass dirty checking and L1 cache — clear the session after bulk ops.


41. What is the Hibernate StatelessSession?

StatelessSession bypasses L1 cache, dirty checking, and interceptors — designed for batch processing.

try (StatelessSession ss = sf.openStatelessSession()) {
    Transaction tx = ss.beginTransaction();
    ScrollableResults<Product> products = ss.createQuery(
        "FROM Product", Product.class)
        .setFetchSize(1000)
        .scroll(ScrollMode.FORWARD_ONLY);

    while (products.next()) {
        Product p = products.get();
        p.setPrice(p.getPrice() * 0.9);
        ss.update(p);         // explicit update required
    }
    tx.commit();
}
Aspect Session StatelessSession
L1 cache Yes No
Dirty checking Yes No
Cascades Yes No
Interceptors Yes No
Use for Normal CRUD Batch ETL, large dataset processing

42. How do you enable Hibernate SQL logging?

# application.properties (Spring Boot)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE  # show bind params

For production monitoring, use P6Spy or Datasource Proxy — they capture actual SQL with parameters without Hibernate's verbose logging.


43. What is the @Formula annotation?

@Entity
public class Order {
    @Id Long id;

    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;

    @Formula("(SELECT SUM(i.quantity * i.price) FROM order_item i WHERE i.order_id = id)")
    private BigDecimal totalAmount;  // computed column, read-only
}

@Formula is Hibernate-specific — it embeds a SQL fragment evaluated on every SELECT. Use sparingly; prefer @Transient with service-layer computation for complex logic.


Spring Data JPA Integration

44. How does Spring Data JPA relate to Hibernate?

Spring Data JPA
    │ (uses)
    ▼
JPA (jakarta.persistence)
    │ (implemented by)
    ▼
Hibernate (default in Spring Boot)
    │
    ▼
JDBC → Database

Spring Data JPA adds repositories, query derivation, pagination, and auditing on top of JPA/Hibernate. You rarely touch Session directly in Spring apps — you use JpaRepository.


45. How do you write efficient queries in Spring Data JPA?

public interface ProductRepository extends JpaRepository<Product, Long> {

    // Derived query — simple
    List<Product> findByPriceLessThan(BigDecimal maxPrice);

    // JPQL — join fetch to avoid N+1
    @Query("SELECT p FROM Product p JOIN FETCH p.category WHERE p.price < :max")
    List<Product> findCheapWithCategory(@Param("max") BigDecimal max);

    // Projection — only fetch needed columns
    interface ProductSummary {
        String getName();
        BigDecimal getPrice();
    }
    List<ProductSummary> findByStatus(ProductStatus status);

    // Pagination
    Page<Product> findByCategory_Name(String categoryName, Pageable pageable);
}

46. What is @Modifying and when is it required?

@Modifying
@Transactional
@Query("UPDATE Product p SET p.price = p.price * :factor WHERE p.category.name = :cat")
int applyDiscount(@Param("factor") BigDecimal factor, @Param("cat") String category);

@Modifying is required for UPDATE, DELETE, or INSERT JPQL queries in Spring Data JPA. Without it, Spring assumes a SELECT and throws an exception at runtime.

Add clearAutomatically = true if L1 cache should be flushed after the bulk operation.


Advanced Topics

47. What is the Hibernate Envers?

Hibernate Envers automatically tracks audit history — every change to an entity is versioned in a _AUD table.

@Entity
@Audited
public class Product {
    @Id Long id;
    String name;
    BigDecimal price;
}
// Hibernate Envers creates: product_aud(id, rev, revtype, name, price)

// Query history
AuditReader reader = AuditReaderFactory.get(session);
Product historicalVersion = reader.find(Product.class, productId, revisionNumber);
List<Number> revisions = reader.getRevisions(Product.class, productId);

48. What is @ElementCollection?

@Entity
public class User {
    @Id Long id;
    String name;

    @ElementCollection
    @CollectionTable(name = "user_phone",
                     joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "phone_number")
    private List<String> phoneNumbers;

    @ElementCollection
    @CollectionTable(name = "user_address")
    private List<Address> addresses;  // @Embeddable Address
}

@ElementCollection maps a collection of value types (primitives, @Embeddable) that have no independent lifecycle — always cascade ALL, always orphan-removed.


49. What is the difference between merge() and update()?

Product detached = ...;   // detached entity with id = 5

// update() — re-attaches the SAME object; throws if another instance with id=5 is in Session
session.update(detached);   // detached now IS the persistent object

// merge() — copies state onto a DIFFERENT managed object; returns managed copy
Product managed = session.merge(detached);   // managed != detached
// detached remains detached

Prefer merge() — safe even if Session already contains an instance with the same ID.


50. What are common Hibernate anti-patterns?

Anti-pattern Problem Fix
N+1 queries 1 + N DB queries for a list JOIN FETCH / @BatchSize / @EntityGraph
Eager loading everywhere Loads data never used Default LAZY; fetch explicitly when needed
open-session-in-view Hidden lazy loads in view layer, connection held Set to false; fetch in service
CascadeType.REMOVE on @ManyToMany Deletes the other side's rows Use orphanRemoval only on @OneToMany
save() instead of persist() May fire immediate INSERT (breaks transactions) Use persist() (JPA standard)
session.update() on already-managed entity NonUniqueObjectException Use session.merge()
Using @GeneratedValue(AUTO) in prod May choose TABLE strategy (slow) Specify IDENTITY or SEQUENCE explicitly
List in @ManyToMany Duplicate join-table rows Use Set

Hibernate vs Related Technologies

Aspect Hibernate Spring Data JPA MyBatis JOOQ
Type Full ORM Repository layer on JPA SQL mapper (no ORM) Type-safe SQL DSL
SQL control Generated Generated / @Query Full SQL control Full SQL control
N+1 risk Yes Yes No (explicit joins) No
Learning curve High Medium Low Medium
Complex queries HQL/Criteria JPQL/@Query/Specification Native SQL in XML Java fluent API
Bulk operations @Modifying @Modifying Efficient Efficient
Best for Domain-rich model, CRUD Spring apps, CRUD Legacy SQL, reporting Type-safe complex SQL

Common mistakes

Mistake Problem Fix
LazyInitializationException Accessing proxy outside closed Session Load data inside @Transactional; disable open-in-view
Bidirectional sync missing One side of relationship not updated Maintain both sides in helper method
equals() / hashCode() on @Id Broken Set and HashMap behavior when entity is transient Use natural business key or UUID
Forgetting @Transactional on write methods TransactionRequiredException Add @Transactional on service methods
SELECT in loop N+1 hidden in service code FETCH all needed data before loop
session.flush() before query in same tx Needed to see own changes in HQL Expected behavior; design tx boundaries carefully
Mapping @OneToOne without @MapsId Extra FK column and potential N+1 Use @MapsId for shared PK
Heavy entities in API responses Over-fetching, security leaks Use projections / DTOs

Frequently asked questions

Q: Should I use Hibernate or MyBatis?
Hibernate for domain-rich models where you want the ORM to manage relationships and caching. MyBatis when you have complex SQL that's easier to write raw, or working with a legacy schema not designed around OOP.

Q: What is the difference between Hibernate and Spring Data JPA?
Spring Data JPA is a higher-level abstraction that adds repositories, query derivation, and pagination on top of JPA (implemented by Hibernate). You're using both simultaneously in most Spring Boot apps.

Q: How do I fix LazyInitializationException?
Load the association inside a @Transactional method, use JOIN FETCH in the query, annotate with @EntityGraph, or set spring.jpa.open-in-view=false and load everything in the service layer before returning to the controller.

Q: Is @Column(nullable = false) the same as a NOT NULL DB constraint?
nullable = false adds a NOT NULL constraint when hibernate.hbm2ddl.auto generates the schema. It also enables Bean Validation at the application layer when combined with @NotNull. For production, manage schema via Flyway/Liquibase.

Q: When should I use native SQL vs HQL?
Use HQL/JPQL for standard CRUD and portability. Use native SQL for DB-specific features (full-text search, JSON operators, window functions, LATERAL JOIN), bulk imports, or when you need a stored procedure.

Q: How do I tune Hibernate for performance in production?

  1. Set spring.jpa.open-in-view=false. 2. Use FetchType.LAZY everywhere; fetch explicitly. 3. Enable L2 cache for frequently-read, rarely-changed entities. 4. Use @BatchSize or JOIN FETCH to eliminate N+1. 5. Enable SQL logging in dev and review every query. 6. Use StatelessSession for batch jobs. 7. Use a connection pool (HikariCP — Spring Boot default).

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools