Interview Questions
Hibernate Interview Questions and Answers
Hibernate questions often try to catch out candidates who've only used it through Spring Data JPA's generated methods without understanding what's happening underneath -- entity states, lazy loading, and the N+1 query problem come up constantly.
Example: The classic N+1 query problem
Java// If Author has a @OneToMany List<Book> books (lazy by default):
List<Author> authors = authorRepository.findAll(); // 1 query
for (Author author : authors) {
System.out.println(author.getBooks().size()); // 1 additional query PER author
}
// Total: 1 + N queries instead of 1
// Fix: fetch books eagerly in the same query with a JOIN FETCH
@Query("SELECT a FROM Author a JOIN FETCH a.books")
List<Author> findAllWithBooks();
Frequently Asked Questions
Transient (a plain Java object, not yet associated with a Hibernate session or saved to the database), Persistent/Managed (associated with an active session -- Hibernate tracks changes to it), Detached (was persistent, but the session that loaded it has closed -- changes to it won't be automatically saved), and Removed (marked for deletion).
By default, @OneToMany and @ManyToMany relationships are lazy -- the related data isn't fetched from the database until you actually access it (e.g. calling .getBooks()). If you try to access it after the Hibernate session has already closed (a common mistake outside a transactional context), you get a LazyInitializationException, since Hibernate can no longer run the query to fetch it.
It happens when you fetch a list of N parent entities, then lazily access a related collection on each one inside a loop -- Hibernate runs one query for the parent list, then one additional query per parent to fetch its related data, for N+1 total queries instead of one or two efficient ones. Fixed with JOIN FETCH, @EntityGraph, or batch fetching.
The first-level cache is per-session and always on -- within one Hibernate session, loading the same entity twice by ID returns the same cached instance without hitting the database again. The second-level cache is optional, shared across sessions, and needs explicit configuration (e.g. with Ehcache) -- it can significantly reduce database load for read-heavy, rarely-changing data.
persist() makes a transient entity managed (schedules an INSERT, executed on flush) and returns void. save() is a Hibernate-specific method similar to persist() but returns the generated ID immediately. merge() takes a detached entity and copies its state onto a managed instance (potentially loading it from the database first), returning that managed instance -- the original detached object stays detached.
It wraps the annotated method in a database transaction and keeps the Hibernate session open for its duration. This is why lazy-loaded associations work fine when accessed inside a @Transactional service method, but throw LazyInitializationException when accessed later (e.g. in a JSP after the transaction has already committed and the session closed).
For a managed entity, Hibernate compares its current field values against a snapshot taken when it was loaded, at flush/commit time. If any field changed, Hibernate generates and runs the corresponding UPDATE automatically -- you don't need to explicitly call a save/update method just to persist a change to an already-managed entity.