THE MODN CHRONICLES

Interview Prep

Interview Questions on Hibernate — ORM, Session Lifecycle, Caching, N+1 Problem & HQL

Hibernate is the default ORM for Java in India. Every Spring Boot project uses Hibernate/JPA under the hood. If you are interviewing for any Java backend role — service company or product company — Hibernate questions are guaranteed. Here are the 10 questions that actually get asked.

Java backend development and Hibernate ORM

Hibernate powers nearly every Java backend in India. From TCS to Flipkart, ORM knowledge is non-negotiable.

Hibernate in Indian IT Interviews

Hibernate is the default ORM for Java in India. Every Spring Boot project uses Hibernate/JPA. Whether you are building microservices at a product company or maintaining enterprise applications at a service company, Hibernate is the data access layer. It is tested in every Java backend interview — from fresher rounds to architect-level discussions.

Interviewers test three areas: ORM fundamentals (what Hibernate does and why), session and caching mechanics (how it works internally), and performance optimization (how to use it without killing your database). Freshers get annotation and mapping questions. Experienced candidates get N+1 problems, caching strategies, and batch processing scenarios.

This guide covers the 10 Hibernate questions that actually appear in Indian interviews — with code examples and architecture explanations.

“What is the difference between Hibernate and JPA?” — this is the opening question in almost every Java backend interview. Get it wrong and the interviewer assumes you have only copy-pasted annotations.

ORM Basics

Q1: What is Hibernate? How is it different from JDBC?

Hibernate is an ORM (Object-Relational Mapping) framework that maps Java objects to database tables. Instead of writing raw SQL and manually mapping ResultSets to objects, Hibernate handles the translation automatically.

Hibernate vs JDBC — Comparison:

JDBC (Manual):                    Hibernate (ORM):
─────────────────────────────     ─────────────────────────────
Write SQL manually                Object-oriented queries (HQL)
Manual ResultSet → Object         Automatic mapping (@Entity)
No caching                        First-level + second-level cache
Manual connection management      Built-in connection pooling
Manual transaction handling       Declarative transactions
No lazy loading                   Lazy loading by default
Database-specific SQL             Database-independent (dialect)

// JDBC approach:
String sql = "SELECT * FROM employees WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 101);
ResultSet rs = ps.executeQuery();
Employee emp = new Employee();
emp.setId(rs.getInt("id"));
emp.setName(rs.getString("name"));

// Hibernate approach:
Employee emp = session.get(Employee.class, 101);
// That's it. One line.

Q2: What is the difference between Hibernate and JPA?

JPA = specification (interface/contract)
Hibernate = implementation (actual code)

JPA defines the standard annotations:
  @Entity, @Table, @Id, @Column,
  @OneToMany, @ManyToOne, etc.

Hibernate implements those annotations.

Other JPA implementations:
  - EclipseLink (reference implementation)
  - OpenJPA (Apache)
  - TopLink (Oracle)

In practice:
  - Everyone uses JPA annotations
  - Hibernate is the runtime underneath
  - Spring Boot auto-configures Hibernate as JPA provider

// You write JPA code:
@Entity
@Table(name = "employees")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "full_name")
    private String name;
}

// Hibernate executes it behind the scenes.
// This is why you see "Hibernate" in logs
// but use "javax.persistence" annotations.

Session & Caching

Q3: Explain the Hibernate Session lifecycle. What are the object states?

Hibernate Object States:

  Transient → Persistent → Detached → Removed

1. TRANSIENT: new object, not in DB, not tracked
   Employee emp = new Employee();  // transient
   emp.setName("Rahul");

2. PERSISTENT: associated with session, tracked by Hibernate
   session.save(emp);              // now persistent
   // Any changes to emp are auto-synced to DB
   emp.setName("Rahul Kumar");     // auto-updated!

3. DETACHED: was persistent, session is closed
   session.close();                // emp is now detached
   emp.setName("New Name");        // NOT tracked anymore
   // To re-attach:
   session.update(emp);            // or session.merge(emp)

4. REMOVED: marked for deletion
   session.delete(emp);            // marked for removal
   // Deleted from DB on flush/commit

State transitions:
  new Object()     → Transient
  session.save()   → Persistent
  session.close()  → Detached
  session.delete() → Removed
  session.merge()  → Detached → Persistent
  session.update() → Detached → Persistent

Q4: What is the difference between get() and load()?

get() vs load():

get():
  - Hits database IMMEDIATELY
  - Returns null if not found
  - Returns the actual object
  - Use when you NEED the object now

  Employee emp = session.get(Employee.class, 101);
  // SQL fires immediately
  // emp is null if ID 101 doesn't exist

load():
  - Returns a PROXY (no DB hit yet)
  - Hits DB only when you access a property
  - Throws ObjectNotFoundException if not found
  - Use when you just need a reference (FK assignment)

  Employee emp = session.load(Employee.class, 101);
  // No SQL yet — emp is a proxy
  emp.getName();
  // NOW SQL fires (lazy loading)

When to use load():
  // Assigning a foreign key without loading the object
  Department dept = session.load(Department.class, 5);
  employee.setDepartment(dept);
  // No need to load the full Department object
  // Just need the reference for the FK

Q5: Explain Hibernate caching levels.

Hibernate Caching Architecture:

┌─────────────────────────────────────────┐
│           Application Layer             │
├─────────────────────────────────────────┤
│  First-Level Cache (Session Cache)      │
│  - Scope: per Session                   │
│  - Automatic, cannot be disabled        │
│  - Same entity loaded twice = 1 SQL     │
│  - Cleared when session closes          │
├─────────────────────────────────────────┤
│  Second-Level Cache (SessionFactory)    │
│  - Scope: shared across all sessions    │
│  - Optional, must be configured         │
│  - Providers: EhCache, Redis, Hazelcast │
│  - Great for read-heavy, rarely-changed │
│  - @Cacheable on entity class           │
├─────────────────────────────────────────┤
│  Query Cache                            │
│  - Caches query RESULTS (not entities)  │
│  - Used with second-level cache         │
│  - query.setCacheable(true)             │
│  - Invalidated when underlying table    │
│    is modified                          │
├─────────────────────────────────────────┤
│           Database                      │
└─────────────────────────────────────────┘

First-level cache example:
  Employee e1 = session.get(Employee.class, 1); // SQL
  Employee e2 = session.get(Employee.class, 1); // NO SQL
  // e1 == e2 (same object reference)

Second-level cache config:
  @Entity
  @Cacheable
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  public class Country { ... }
  // Country data cached across all sessions

Mapping & Relationships

Q6: How do you map relationships in Hibernate?

Relationship Annotations:

// @OneToOne
@Entity
public class Employee {
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
}

// @OneToMany / @ManyToOne (most common)
@Entity
public class Department {
    @OneToMany(mappedBy = "department",
               cascade = CascadeType.ALL)
    private List<Employee> employees;
}

@Entity
public class Employee {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id")
    private Department department;
}

// @ManyToMany
@Entity
public class Student {
    @ManyToMany
    @JoinTable(name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id"))
    private Set<Course> courses;
}

// Bidirectional: use mappedBy on the non-owning side
// Cascade types: ALL, PERSIST, MERGE, REMOVE,
//                REFRESH, DETACH
// FetchType: LAZY (default for collections),
//            EAGER (default for @ManyToOne)

Q7: What is the N+1 problem? How do you fix it?

The N+1 Problem:

1 query for parent + N queries for each child = N+1

Example: Fetch 100 orders, each triggers a query
for its customer:

  // BAD: N+1 problem
  List<Order> orders = session.createQuery(
      "FROM Order").list();
  // 1 SQL: SELECT * FROM orders (100 rows)

  for (Order o : orders) {
      o.getCustomer().getName();
      // 100 SQLs: SELECT * FROM customers WHERE id = ?
  }
  // Total: 101 queries!

Fix 1: JOIN FETCH in HQL
  List<Order> orders = session.createQuery(
      "FROM Order o JOIN FETCH o.customer").list();
  // 1 SQL with JOIN — done!

Fix 2: @BatchSize
  @OneToMany
  @BatchSize(size = 25)
  private List<OrderItem> items;
  // Loads 25 children at a time instead of 1

Fix 3: EntityGraph (JPA 2.1)
  @EntityGraph(attributePaths = {"customer", "items"})
  List<Order> findAll();
  // Defines what to fetch eagerly per query

Fix 4: Subselect fetching
  @Fetch(FetchMode.SUBSELECT)
  private List<OrderItem> items;
  // Uses IN (SELECT ...) instead of N queries
Java developer working on Hibernate ORM code

Understanding session lifecycle and caching is what separates junior developers from senior Hibernate engineers.

HQL & Performance

Q8: What is HQL? How is it different from SQL?

HQL = Hibernate Query Language
Object-oriented query language that uses
class/property names, NOT table/column names.

HQL vs SQL:

HQL:
  FROM Employee e WHERE e.salary > 50000
  // Uses class name "Employee", property "salary"

SQL:
  SELECT * FROM employees WHERE salary > 50000
  // Uses table name "employees", column "salary"

HQL features:
  - Polymorphism: FROM Payment → returns all subtypes
  - Pagination: query.setFirstResult(0).setMaxResults(10)
  - Named parameters: WHERE e.name = :name
  - Joins: FROM Employee e JOIN e.department d
  - Projections: SELECT e.name, e.salary FROM Employee e
  - Aggregates: SELECT AVG(e.salary) FROM Employee e

// Named query (reusable, pre-compiled)
@NamedQuery(
    name = "Employee.findByDept",
    query = "FROM Employee e WHERE e.department.name = :dept"
)

// Usage:
Query q = session.getNamedQuery("Employee.findByDept");
q.setParameter("dept", "Engineering");
List<Employee> results = q.list();

Q9: What is the difference between Criteria API and HQL?

HQL: string-based, like SQL but object-oriented
Criteria: programmatic, type-safe, dynamic queries

HQL — better for static, readable queries:
  String hql = "FROM Employee e WHERE e.salary > :min "
             + "AND e.department.name = :dept";
  // Clean, readable, easy to understand

Criteria API — better for dynamic filters:
  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
  Root<Employee> root = cq.from(Employee.class);

  List<Predicate> predicates = new ArrayList<>();

  if (minSalary != null)
      predicates.add(cb.gt(root.get("salary"), minSalary));
  if (deptName != null)
      predicates.add(cb.equal(
          root.get("department").get("name"), deptName));

  cq.where(predicates.toArray(new Predicate[0]));
  // Dynamic — only adds filters that are provided

When to use what:
  HQL     → fixed queries, reports, simple lookups
  Criteria → search forms, dynamic filters, type-safe

Note: Hibernate's old Criteria API (org.hibernate.Criteria)
is DEPRECATED. Use JPA Criteria API (javax.persistence).

Q10: How do you optimize Hibernate performance?

Hibernate Performance Optimization Checklist:

1. Batch processing:
   hibernate.jdbc.batch_size = 25
   // Groups INSERT/UPDATE into batches
   for (int i = 0; i < 10000; i++) {
       session.save(entities.get(i));
       if (i % 25 == 0) {
           session.flush();
           session.clear(); // prevent memory overflow
       }
   }

2. Lazy loading by default:
   @ManyToOne(fetch = FetchType.LAZY)
   // Don't load what you don't need

3. Use projections (select specific columns):
   SELECT new EmployeeDTO(e.name, e.salary)
   FROM Employee e
   // Don't load entire entity for a dropdown

4. Fix N+1 with JOIN FETCH:
   FROM Order o JOIN FETCH o.customer
   // One query instead of N+1

5. Second-level cache for read-heavy data:
   @Cacheable // countries, states, config
   // Avoid DB hits for rarely-changed data

6. StatelessSession for bulk operations:
   StatelessSession ss = sf.openStatelessSession();
   // No cache, no dirty checking, no cascades
   // Pure speed for batch imports

7. Monitor with logging:
   hibernate.show_sql = true
   hibernate.format_sql = true
   hibernate.generate_statistics = true
   // See exactly what SQL Hibernate generates

How to Prepare

Hibernate Interview — Priority by Experience

Freshers (0-1 years)

  • • ORM basics & annotations
  • • Hibernate vs JDBC vs JPA
  • • Session object states
  • • Basic mappings (@Entity, @Id)
  • • Simple HQL queries

Mid-Level (2-4 years)

  • • Caching levels (L1, L2)
  • • N+1 problem & fixes
  • • Relationship mappings
  • • HQL vs Criteria API
  • • Lazy vs eager loading

Senior (5+ years)

  • • Performance tuning
  • • Batch processing
  • • Multi-tenancy strategies
  • • StatelessSession usage
  • • Cache invalidation patterns

Start Java Backend Mock Interview

Get asked real Hibernate interview questions — ORM concepts, session lifecycle, caching strategies, and performance optimization. Practice explaining architecture with code examples.

Free · AI-powered feedback · Hibernate questions