skip to content

Auditing Metadata

Auditing annotations fill created and modified timestamps and users automatically, given an enabled auditing config and an AuditorAware that supplies the current principal. Interviewers ask how the current user reaches the persistence layer, and this is where it lands.

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

questions

5

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

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

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

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

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