skip to content

Paging, Sorting, Projections & Auditing

The cross-store query features you use daily: paging and sorting, Page versus Slice and keyset scrolling, interface and DTO projections, and auditing metadata. Interviewers use this area to test whether you fetch only what you need.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

24

What are the four JPA auditing annotations in Spring Data, and what two things must you configure to make them populate automatically?

level: juniorimportance: must knowfreq 70%

answer

  1. 4 annotations: CreatedDate/LastModifiedDate/CreatedBy/LastModifiedBy
  2. @EnableJpaAuditing turns it on globally
  3. @EntityListeners(AuditingEntityListener.class) on entity
  4. dates auto; who needs AuditorAware
  5. @MappedSuperclass base class idiom

basics

~10 s

The annotations are @CreatedDate, @LastModifiedDate, @CreatedBy, and @LastModifiedBy on entity fields. To activate them you add @EnableJpaAuditing on a config class and attach @EntityListeners(AuditingEntityListener.class) to the entity.

solid answer

~30 s

Spring Data JPA fills four fields for you: @CreatedDate and @LastModifiedDate (timestamps) and @CreatedBy and @LastModifiedBy (the acting principal). Two pieces wire this up. First, @EnableJpaAuditing on any @Configuration class turns the feature on globally. Second, each audited entity needs @EntityListeners(AuditingEntityListener.class), which registers the JPA listener that sets those fields during persist and update lifecycle callbacks. Dates work out of the box; the @CreatedBy/@LastModifiedBy fields stay null unless you also provide an AuditorAware<T> bean that returns the current user. You typically put the four fields on a shared @MappedSuperclass base class so every entity inherits them without repetition.

code

java · 29 lines
java
@Configuration
@EnableJpaAuditing
class JpaAuditingConfig { }

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable {

    @CreatedDate
    @Column(updatable = false)
    private Instant createdDate;

    @LastModifiedDate
    private Instant lastModifiedDate;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
    // getters/setters
}

@Entity
public class Article extends Auditable {
    @Id @GeneratedValue Long id;
    String title;
}

go deeper

for a junior

Name the four annotations and the two wiring steps (@EnableJpaAuditing + @EntityListeners); know dates auto-fill.

for a middle

Add that @CreatedBy needs AuditorAware, and that the @MappedSuperclass base class is the standard placement.

for a senior

Discuss updatable=false on created columns, correct field types, and the org.springframework.data.annotation package distinction.

for a principal

Frame auditing as one option among Envers, DB triggers, and event-sourcing; know its consistency and testability trade-offs.

## What auditing metadata is "Auditing" here means automatically recording *who* touched a row and *when*, without writing that code in every service. Spring Data JPA provides four field-level annotations: - **`@CreatedDate`** — set once, when the entity is first persisted (INSERT). - **`@LastModifiedDate`** — set on the initial persist and re-set on every update. - **`@CreatedBy`** — the principal (user) who created the row, set once. - **`@LastModifiedBy`** — the principal who last modified the row, re-set on every update. All four live in package `org.springframework.data.annotation` (note: *not* the JPA package), so they are Spring Data annotations, not standard JPA. ## The two required wiring steps **1. Enable the feature.** Put `@EnableJpaAuditing` on a `@Configuration` class (often the main application class). This registers the infrastructure beans that make auditing work. Without it, the annotations are simply ignored. **2. Register the listener on the entity.** Annotate each audited entity with `@EntityListeners(AuditingEntityListener.class)`. `AuditingEntityListener` is a JPA entity listener that hooks the `@PrePersist` and `@PreUpdate` lifecycle events and writes the four fields at those moments. ## Field types - Date/time fields may be `Instant`, `LocalDateTime`, `Date`, `Long` (epoch millis), or Java 8 date types — Spring converts appropriately. - `@CreatedBy`/`@LastModifiedBy` are typed to whatever your `AuditorAware<T>` returns — commonly `String` (username) or a `Long`/`UUID` user id. ## The auditor piece Timestamps need no extra config — the framework reads the clock. But *who* the current user is, Spring cannot know. You supply an `AuditorAware<T>` bean whose `getCurrentAuditor()` returns an `Optional<T>` of the current principal (typically pulled from Spring Security's `SecurityContextHolder`). If you omit it, `@CreatedBy`/`@LastModifiedBy` stay null but timestamps still populate. ## Where to put the fields Repeating four fields on every entity is noise. The idiom is a `@MappedSuperclass` base class (e.g. `Auditable`) holding the four fields plus `@EntityListeners(AuditingEntityListener.class)`, which all entities extend. `@MappedSuperclass` means the base's columns are mapped into each subclass's table without the base being an entity itself. ## Common gotchas - Forgetting `@EnableJpaAuditing` — fields silently stay null. - Forgetting `@EntityListeners` on the entity (or on the base class) — same silent null. - Expecting `@CreatedBy` to fill without an `AuditorAware` bean. - Using `save()` on a *detached/merge* path and expecting `@CreatedDate` — created is only set on the first persist.

  • Which import package do @CreatedDate and friends come from, and why does it matter?
    org.springframework.data.annotation — they are Spring Data annotations, not jakarta.persistence. Importing the wrong same-named type (there isn't a JPA equivalent, but IDEs sometimes auto-import odd things) means nothing populates.
  • If you enable auditing but never define an AuditorAware bean, what happens?
    Timestamps (@CreatedDate/@LastModifiedDate) still populate from the clock, but @CreatedBy/@LastModifiedBy stay null because Spring has no source for the current principal.

saying these in an interview costs you the question

  • Thinking @EnableJpaAuditing alone is enough without @EntityListeners on the entity
  • Believing the annotations come from jakarta.persistence / standard JPA
  • Assuming @CreatedBy fills automatically without an AuditorAware bean
  • Confusing this with Hibernate Envers (which is full row-versioning/history, a different feature)

context

open as a page

What is a class-based DTO projection in Spring Data, and why use one instead of returning the entity?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A repository query method returns a plain data-carrier class (often a record) holding only the fields you need, instead of the full entity. Spring Data fetches only those columns, so it is smaller and faster.

open as a page

What is an interface-based projection in Spring Data, and why would you use one instead of returning the full entity?

level: juniorimportance: must knowfreq 72%

basics

~20 s

An interface-based projection is a plain Java interface with getters. You declare it as a repository method's return type; Spring Data returns proxies exposing only those getters, so you fetch just the columns you need instead of the whole entity.

open as a page

What is the difference between returning a Page<T> and a Slice<T> from a Spring Data repository method?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A Page knows the total number of results (total elements and total pages), so it runs an extra COUNT query. A Slice only knows whether there is a next page, so it skips the COUNT query and is cheaper.

open as a page

What is Pageable in Spring Data, and how do you create and use a PageRequest to fetch one page of results?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Pageable describes which slice to fetch — a page number and a page size. You create one with PageRequest.of(page, size) and pass it to a repository method. Page numbers are 0-based, so page 0 is the first page.

open as a page

How does @CreatedBy / @LastModifiedBy get the current user? Explain AuditorAware and how you'd implement it with Spring Security.

level: middleimportance: must knowfreq 68%

basics

~20 s

You implement AuditorAware<T>, a functional interface whose getCurrentAuditor() returns Optional of the current user. Register it as a bean; Spring calls it during persist/update to fill @CreatedBy and @LastModifiedBy. With Spring Security you read the principal from SecurityContextHolder.

open as a page

How do you return a DTO from a custom @Query using a JPQL constructor expression, and what are its constraints?

level: middleimportance: must knowfreq 68%

basics

~10 s

In JPQL use select new com.example.MyDto(u.firstname, u.lastname) from User u. You must give the DTO's fully-qualified class name and pass constructor arguments in the exact order and types the constructor expects.

open as a page

What is the difference between a closed and an open interface projection, and how does each affect the generated SQL?

level: middleimportance: must knowfreq 68%

basics

~20 s

Closed projections have only plain getters that map to entity properties, so Spring narrows the SELECT to just those columns. Open projections use @Value with SpEL (or default methods computing values), so Spring can't optimize and fetches the whole entity.

open as a page

How do you build multi-field sorting with Sort, Sort.Order and Sort.Direction, including case-insensitive and null-handling options?

level: middleimportance: must knowfreq 58%

basics

~10 s

Use Sort.by(...) to name properties. For fine control, build Sort.Order objects — Order.asc("x"), Order.desc("y") — combine them with Sort.by(order1, order2), and refine each with .ignoreCase() or .nullsLast(). Direction is Sort.Direction.ASC or DESC.

open as a page

Where do you place the audit fields and the AuditingEntityListener, and what configuration mistakes make auditing silently fail?

level: middleimportance: should knowfreq 45%

basics

~10 s

Put the four fields plus @EntityListeners(AuditingEntityListener.class) on a shared @MappedSuperclass base class every entity extends. Silent failures usually come from missing @EnableJpaAuditing, missing @EntityListeners, or no AuditorAware bean for the *By fields.

open as a page

When would you deliberately choose Slice over Page, and what performance problem does that solve?

level: middleimportance: should knowfreq 55%

basics

~20 s

Choose Slice when the UI only needs 'is there more?' (infinite scroll, load-more) and never shows a total. It avoids the extra COUNT query that Page runs, which can be very expensive on large or joined tables.

open as a page

Under the hood, what SQL/fetch behavior distinguishes a Slice query from a Page query, and what subtle bug can the limit+1 trick expose?

level: middleimportance: should knowfreq 35%

basics

~20 s

A Slice fetches pageSize + 1 rows in a single query; if the extra row exists, hasNext() is true and that row is trimmed off. A Page runs the same data fetch plus a separate SELECT COUNT to compute totals.

open as a page

How do you bound a result set in Spring Data using the Top/First keywords versus the dynamic Limit parameter?

level: middleimportance: should knowfreq 42%

basics

~20 s

Static bound: put Top or First plus a number in the method name, e.g. findTop10ByOrderByScoreDesc. Dynamic bound: add a Limit parameter — Limit.of(n) — passed at call time. Both cap how many rows come back.

open as a page

Explain the mechanism behind JPA auditing: what AuditingEntityListener actually does, when fields are written, and why bulk updates and native queries escape it.

level: seniorimportance: should knowfreq 50%

basics

~20 s

AuditingEntityListener is a JPA entity listener hooked to @PrePersist and @PreUpdate. When the persistence provider fires those callbacks, the listener sets the timestamp and auditor fields. Bulk JPQL and native SQL bypass the lifecycle, so no callback fires and audit fields aren't updated.

open as a page

What are dynamic projections in Spring Data, and how does the generic <T> method parameter choose the result shape at call time?

level: seniorimportance: should knowfreq 55%

basics

~20 s

A dynamic projection is one query method that can return different shapes depending on a Class<T> argument you pass at call time. You write <T> List<T> findByLastname(String lastname, Class<T> type) and call it with the DTO, interface, or entity class you want.

open as a page

How do nested interface projections work, and what determines whether the associated data is fetched efficiently?

level: seniorimportance: should knowfreq 48%

basics

~20 s

A getter can return another projection interface, so you project across associations. Spring resolves the nested interface's getters against the associated entity. Nesting works cleanly for closed projections built from entity properties; native/complex queries and open expressions can break the optimization.

open as a page

When would you choose an interface projection versus a class-based (DTO) projection, and what are dynamic projections?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Interface projections use proxies and getters and support open/nested projections. Class (DTO) projections use a constructor and give you a concrete, serializable value type. Dynamic projections let one repository method return different projection types via a Class<T> parameter.

open as a page

What are Window<T> and ScrollPosition, and how does keyset scrolling avoid the cost of large-offset pagination?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Window<T> is a result chunk returned by the Spring Data Scroll API; ScrollPosition tells the query where to resume. With KeysetScrollPosition, the query resumes using WHERE last_sort_value conditions instead of OFFSET, so the database seeks via an index rather than scanning and skipping millions of rows.

open as a page

As an architect, when would you choose Spring Data @CreatedBy/@LastModifiedDate auditing versus Hibernate Envers or database triggers? What are the trade-offs and failure modes?

level: principalimportance: should knowfreq 35%

basics

~20 s

Spring Data auditing stamps who/when on the live row — simple, in-app, but only current state and only for entity-lifecycle writes. For full change history use Hibernate Envers; for a guarantee that covers every writer (including bulk SQL and other apps) use database triggers.

open as a page

When would you choose a class-based DTO projection over an interface projection, and what are the key tradeoffs and pitfalls at scale?

level: principalimportance: should knowfreq 40%

basics

~20 s

Choose a class/record DTO when you want a concrete, immutable, detached object you can pass around and serialize freely. Choose an interface projection when you want lightweight read-only views or need open SpEL-computed values. Both closed forms cut columns.

open as a page

You added a closed interface projection but the SQL still selects all columns. What are the likely causes, and how do you guarantee column narrowing?

level: principalimportance: should knowfreq 30%

basics

~20 s

Column narrowing only happens automatically for derived query methods over a closed projection. A manual @Query (JPQL or native) selects exactly what you wrote, an @Value getter makes it open, or fetching the entity then mapping defeats it. To guarantee narrowing, use a closed projection on a derived method or explicitly select only the needed columns.

open as a page

Why can offset pagination skip or duplicate rows, and how do Page, Slice, sort stability, and keyset pagination address the trade-offs at scale?

level: principalimportance: should knowfreq 30%

basics

~20 s

Offset paging (page*size) is only correct if the sort order is total and stable — otherwise ties or concurrent inserts shift rows and pages overlap or skip. Add a unique tie-breaker like id. Page runs a costly count; deep offsets scan far. For large data, use keyset pagination.

open as a page

What is Sort.TypedSort and what advantage does it give over Sort.by(String...)?

level: seniorimportance: nice to knowfreq 18%

basics

~10 s

Sort.TypedSort lets you build a Sort from method references instead of String property names, so sort fields are checked by the compiler and survive refactoring/renames. You get it via Sort.sort(EntityClass.class).

open as a page

You're designing a public cursor-paginated API over a very large, frequently-changing table. Compare Page, Slice, and keyset Window scrolling and justify a choice, including their failure modes.

level: principalimportance: nice to knowfreq 25%

basics

~20 s

For a large, changing table with a public cursor API, use keyset Window scrolling: it gives stable forward cursors and constant per-page cost. Page's count query and OFFSET are too costly and OFFSET drifts as rows change; Slice fixes the count but not OFFSET drift.

open as a page