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:
mappedByon the non-owning side (parent) — no extra join table column- Owning side = the side with
@JoinColumn(the FK column) orphanRemoval = true— deletesOrderItemwhen 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?
- Set
spring.jpa.open-in-view=false. 2. UseFetchType.LAZYeverywhere; fetch explicitly. 3. Enable L2 cache for frequently-read, rarely-changed entities. 4. Use@BatchSizeorJOIN FETCHto eliminate N+1. 5. Enable SQL logging in dev and review every query. 6. UseStatelessSessionfor batch jobs. 7. Use a connection pool (HikariCP — Spring Boot default).