Spring Data JPA Interview Questions
Commonly asked Spring Data JPA interview questions with clear, practical answers.
A curated set of Spring Data JPA interview questions, covering the repository abstraction, transactions, and one of the most commonly asked performance gotchas.
Q: How do Spring Data JPA's derived query methods actually work?
Spring Data JPA parses the repository method's name at application startup, splitting it against known prefixes (findBy, countBy, existsBy) and keywords (And, OrderBy, Containing, GreaterThan) to build a query targeting the entity's actual field names — for example, findByEmailAndActiveTrue becomes a query filtering on the email and active fields. Because this parsing happens at startup rather than at call time, a method name that doesn't match any real entity field fails immediately when the application starts, rather than at runtime when the method is first called.
Q: What's the difference between @Query with JPQL and a native query?
JPQL queries entities and their fields (SELECT o FROM Order o WHERE o.status = :status) and is translated into the appropriate SQL for whatever database is configured — it stays portable across database vendors and is aware of entity relationships. A native query (@Query(value = "...", nativeQuery = true)) is raw SQL against actual tables and columns, needed when JPQL can't express something (a vendor-specific function, a highly tuned query) but at the cost of database portability.
Q: What does @Transactional actually guarantee, and where should it typically be placed?
@Transactional wraps a method in a single database transaction: every write inside it commits together on success, or rolls back together if an unchecked exception propagates out of the method. It's conventionally placed on service-layer methods rather than repository or controller methods, because a service method usually represents one coherent business operation — the natural unit of work that should either fully succeed or fully fail. A common gotcha: by default it only rolls back on unchecked (RuntimeException) exceptions, not checked ones, unless rollbackFor is specified explicitly.
Q: What is the N+1 query problem, and how does it happen with lazy loading?
It happens when code loads a list of N parent entities, then accesses a lazily-loaded association on each one individually inside a loop — one query loads the N parents, then N additional queries load each parent's association separately, for N+1 total queries instead of one or two. For example, loading 50 orders and then calling order.getCustomer().getEmail() on each inside a loop triggers 50 extra customer queries. It's fixed by fetching what's needed up front — a JPQL query with JOIN FETCH, or an entity graph — instead of letting each association lazily trigger its own query one at a time.
Q: What's the difference between Page<T> and Slice<T> when paginating results?
Both return one page of results from a Pageable query, but Page<T> additionally runs a separate COUNT query to report the total number of matching rows and total pages (getTotalElements(), getTotalPages()). Slice<T> skips that extra count query and only reports whether a next page exists (hasNext()) — cheaper when the UI only needs "load more" style pagination and doesn't need to display a total count or page number.
Q: How does Spring Data JPA's auditing support (@CreatedDate, @LastModifiedDate) actually populate those fields?
@EnableJpaAuditing registers an AuditingEntityListener, which is attached to an entity (or a shared @MappedSuperclass) via @EntityListeners. That listener hooks into JPA's own lifecycle callbacks (@PrePersist for creation fields, @PreUpdate for modification fields) and populates the annotated fields automatically on every insert/update, with no code needed in the entity itself or any service that saves one. Capturing who made the change (@CreatedBy/@LastModifiedBy) additionally requires an AuditorAware bean telling Spring how to determine the current user — typically read straight from SecurityContextHolder.
Q: What's the difference between a hard delete and the @SQLDelete/@SQLRestriction soft-delete pattern?
A hard delete removes the row permanently via DELETE. The soft-delete pattern instead uses @SQLDelete to override the SQL Hibernate issues on delete with an UPDATE that flags a deleted column, and @SQLRestriction to transparently append AND deleted = false to every query Hibernate generates against that entity — through derived methods, JPQL, and JpaRepository calls alike — so soft-deleted rows are invisible everywhere without a manual filter written anywhere in application code, while the row itself stays intact for audit or recovery purposes.
Q: When would you reach for the Specification API instead of a derived query method?
Derived query methods are ideal for a small, fixed, known set of query shapes, but they don't scale to a search endpoint where any combination of several optional filters might be supplied — that would require one derived method per combination. Specification<T> lets you build small, named, composable predicates and combine only the ones relevant to a given request at runtime (spec.and(hasAuthor(author)), conditionally), which is exactly the shape a dynamic, multi-field search needs. It requires the repository to additionally extend JpaSpecificationExecutor<T>.