What Hibernate is, JPA vs Hibernate, core components, SessionFactory and Session (EntityManagerFactory/EntityManager), transactions, HQL/JPQL, the Criteria API, entity states, bootstrapping, the second-level cache, get() vs load(), and data integrity.
Published September 25, 2026
Answer Hibernate questions in JPA terms first, then add the Hibernate specifics. Modern Spring code uses EntityManager and Spring Data repositories, and Hibernate is the implementation underneath. Knowing both vocabularies (Session ↔ EntityManager) shows you've used it for real.
Short answer: Hibernate is the most widely used Java ORM (object-relational mapping) framework, and the default JPA implementation in Spring Boot. It maps Java classes to tables, and objects to rows. It generates the SQL for CRUD operations, manages entity state and dirty checking, handles relationships, lazy loading and caching, and translates between Java types and SQL types.
Key points to cover:
EntityManager, JPQL. Hibernate is an implementation, with extra features (Envers, custom types, second-level cache integrations).Learn it in depth → N+1 Query Problem
Short answer:
Configuration/bootstrap: reads the settings and mappings.SessionFactory: a thread-safe, expensive factory, one per database.Session: the unit of work, holding the persistence context.Transaction.Query (HQL, native SQL, Criteria).JPA equivalents: EntityManagerFactory, EntityManager, EntityTransaction, TypedQuery.
SessionFactory?Short answer: It's built once at startup from the configuration and mappings. It holds the compiled metadata, the connection provider and the second-level cache, and it creates Sessions. It's thread-safe and heavyweight, so there's one per database per application. In JPA it's the EntityManagerFactory, which Spring Boot creates for you.
Session?Short answer: A short-lived, single-threaded unit of work between the application and the database (JPA: EntityManager). It holds the persistence context, the first-level cache of the entities it has loaded, which tracks their changes. At flush or commit, it writes those changes to the database.
Key points to cover:
EntityManager. It's a proxy bound to the current transaction, so each transaction gets its own persistence context.Short answer: All writes happen inside a transaction. Plain Hibernate uses session.beginTransaction() and commit()/rollback(), on top of either a JDBC connection's transaction or JTA (for distributed or container-managed transactions). In Spring applications you use @Transactional, and Spring's JpaTransactionManager begins, commits or rolls back the transaction, and binds the EntityManager to it.
@Service
class TransferService {
@Transactional
public void transfer(long fromId, long toId, BigDecimal amount) {
Account from = accounts.findById(fromId).orElseThrow();
Account to = accounts.findById(toId).orElseThrow();
from.debit(amount);
to.credit(amount);
// no save() needed: dirty checking flushes both UPDATEs at commit; any RuntimeException rolls back both
}
}
Learn it in depth → @Transactional Deep Dive
Short answer: Hibernate Query Language, the superset of JPA's JPQL. It's an object-oriented query language that queries entities and their properties, not tables and columns. It understands associations, inheritance and polymorphism, and Hibernate translates it into the database's SQL dialect.
List<Order> recent = em.createQuery(
"select o from Order o join fetch o.customer c where c.city = :city and o.createdAt > :since", Order.class)
.setParameter("city", "Pune")
.setParameter("since", Instant.now().minus(7, ChronoUnit.DAYS))
.getResultList();
Key points to cover:
@Query("select o from Order o where …"), or are derived from method names (findByCustomerCityAndCreatedAtAfter).Short answer: A programmatic, type-safe way to build queries in Java code instead of strings. It's ideal for dynamic queries whose filters depend on user input, such as search screens.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Product> q = cb.createQuery(Product.class);
Root<Product> p = q.from(Product.class);
List<Predicate> filters = new ArrayList<>();
if (name != null) filters.add(cb.like(cb.lower(p.get("name")), "%" + name.toLowerCase() + "%"));
if (maxPrice != null) filters.add(cb.le(p.get("price"), maxPrice));
q.where(filters.toArray(Predicate[]::new));
List<Product> results = em.createQuery(q).getResultList();
Key points to cover:
Criteria API was deprecated, and removed in Hibernate 6. Use the JPA Criteria API, or Spring Data Specifications, which wrap it neatly. Querydsl and jOOQ are popular alternatives.Short answer:
Customer c = new Customer("Asha"); // transient
em.persist(c); // managed: INSERT at flush
c.setCity("Pune"); // tracked: UPDATE at flush (dirty checking)
em.detach(c); // detached: further changes are ignored…
Customer merged = em.merge(c); // …until merged back (merge returns the managed copy)
em.remove(merged); // removed: DELETE at flush
Configuration class?Short answer: In native Hibernate, Configuration (or the newer StandardServiceRegistryBuilder + MetadataSources) bootstraps Hibernate. It loads settings (from hibernate.cfg.xml or properties), registers the mapped classes, and builds the SessionFactory.
Key points to cover:
spring.datasource.* and spring.jpa.* properties, and builds the EntityManagerFactory automatically.Short answer: An optional cache shared across sessions, at the SessionFactory level, that stores entity data by ID. Repeat reads of the same entities from different transactions then skip the database. It's provided by a pluggable cache (Ehcache or Infinispan through JCache, or Hazelcast), and enabled per entity with @Cache.
Key points to cover:
get() and load()?Short answer:
session.get() (JPA: em.find()) hits the database immediately (unless the entity is already in the persistence context), and returns null if the row doesn't exist.session.load() (JPA: em.getReference()) returns a lazy proxy without querying. The query runs on first access to a non-ID property, and that is when an ObjectNotFoundException/EntityNotFoundException is thrown if the row is missing.Order order = em.getReference(Order.class, orderId); // no SELECT
line.setOrder(order); // perfect for setting a foreign key without loading the Order
Key points to cover:
load() is deprecated in favour of getReference(). Spring Data exposes it as getReferenceById().Short answer: Through transactions (atomic commit or rollback), database constraints that it maps (primary keys, foreign keys, unique and not-null constraints), optimistic locking with @Version to prevent lost updates, pessimistic locks (SELECT … FOR UPDATE) when needed, and Bean Validation checks before insert or update.
Key points to cover:
Q: What is dirty checking?
A: At flush time, Hibernate compares every managed entity with the snapshot taken when it was loaded, and issues UPDATE statements for the changed ones. That's why save() isn't needed for managed entities inside a transaction.
Q: When does Hibernate flush?
A: At transaction commit, before running a query whose results the pending changes could affect (in AUTO flush mode), or when you call flush() explicitly. A flush sends the SQL, but it doesn't commit.
Q: persist() vs merge()?
A: persist makes a new transient entity managed. merge copies the state of a detached (or new) entity onto a managed instance, and returns that instance. The object you passed in stays detached.
Q: Why prefer FetchType.LAZY for @ManyToOne?
A: @ManyToOne and @OneToOne default to EAGER in JPA, which silently loads related rows on every query, and causes N+1 problems. Declare them LAZY, and fetch explicitly when you need them.