BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Persistence and JPA

Persistence

Auditing, lifecycle callbacks and soft delete

Many tables need "who and when": created and modified timestamps, the user who did it, and sometimes a flag instead of real deletion.

Learn these first

At a glance

Mental model

The sign-in sheet at a reception desk: every entry gets a timestamp and a name without the visitor writing them, and a "deleted" entry is struck through, not torn out.

What happens, in order
  1. 1@PrePersist and @PreUpdate methods on an entity run just before insert or update; they can set createdAt and updatedAt
  2. 2With Spring Data, @EnableJpaAuditing, @EntityListeners(AuditingEntityListener.class) and fields annotated @CreatedDate, @LastModifiedBy do it without code in the entityAn AuditorAware<String> bean supplies the current user from the security context.
  3. 3Soft delete: @SQLDelete(sql = "update orders set deleted = true where id = ?") turns delete into an update, and @SQLRestriction("deleted = false") (older: @Where) hides deleted rows from queries
  4. 4Hibernate Envers (@Audited) goes further and keeps a full revision history table for each change
Remember
Watch out
Forgetting that native queries ignore the soft-delete filterUsing local time for audit columnsHeavy logic inside callbacksBulk updates that skip callbacks and leave timestamps stale
Example (highlighted lines explain it)
@Entity@EntityListeners(AuditingEntityListener.class)@SQLDelete(sql = "update documents set deleted = true where id = ?")@SQLRestriction("deleted = false")public class Document {    @Id @GeneratedValue    private Long id;    private boolean deleted;    @CreatedDate    private Instant createdAt;    @LastModifiedBy    private String modifiedBy;}
  1. line 2 uses EntityListeners, the core of Auditing, lifecycle callbacks and soft delete
  2. line 3 uses SQLDelete, the core of Auditing, lifecycle callbacks and soft delete
  3. line 4 uses SQLRestriction, the core of Auditing, lifecycle callbacks and soft delete
  4. line 10 uses CreatedDate, the core of Auditing, lifecycle callbacks and soft delete
  5. line 13 uses LastModifiedBy, the core of Auditing, lifecycle callbacks and soft delete
Read the full explanation (about 2 min)

Many tables need "who and when": created and modified timestamps, the user who did it, and sometimes a flag instead of real deletion. JPA callbacks and Spring Data auditing fill these in automatically.

A mental model

The sign-in sheet at a reception desk: every entry gets a timestamp and a name without the visitor writing them, and a "deleted" entry is struck through, not torn out.

How it works

@PrePersist and @PreUpdate methods on an entity run just before insert or update; they can set createdAt and updatedAt.

With Spring Data, @EnableJpaAuditing, @EntityListeners(AuditingEntityListener.class) and fields annotated @CreatedDate, @LastModifiedBy do it without code in the entity. An AuditorAware<String> bean supplies the current user from the security context.

Soft delete: @SQLDelete(sql = "update orders set deleted = true where id = ?") turns delete into an update, and @SQLRestriction("deleted = false") (older: @Where) hides deleted rows from queries.

Hibernate Envers (@Audited) goes further and keeps a full revision history table for each change.

Going deeper

Soft delete interacts badly with unique constraints (a "deleted" email still blocks a new account) and with every native query, which must add the filter by hand. Weigh it against an archive table.

Callbacks run inside the persistence context; they should not call repositories or other entities. Keep them to setting fields.

Store timestamps as UTC Instant, not local time, so audit data does not change meaning across time zones.

Under the hood

Hibernate fires lifecycle events during flush. JPA callback methods are discovered by reflection once per entity class and invoked through event listeners; Spring Data auditing is just one more entity listener that reads the auditor and clock and writes the annotated fields before the insert or update runs.

Common mistakes

  • Forgetting that native queries ignore the soft-delete filter.
  • Using local time for audit columns.
  • Heavy logic inside callbacks.
  • Bulk updates that skip callbacks and leave timestamps stale.

How Unravel recognises it

When you paste code, Unravel looks for these signs of auditing, lifecycle callbacks and soft delete. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Auditing, lifecycle callbacks and soft delete?

Many tables need "who and when": created and modified timestamps, the user who did it, and sometimes a flag instead of real deletion. JPA callbacks and Spring Data auditing fill these in automatically.

What are the most common mistakes with Auditing, lifecycle callbacks and soft delete?

Forgetting that native queries ignore the soft-delete filter. Using local time for audit columns. Heavy logic inside callbacks. Bulk updates that skip callbacks and leave timestamps stale.

How can I see Auditing, lifecycle callbacks and soft delete in real code?

Paste your own Java into Unravel and it will point out where auditing, lifecycle callbacks and soft delete appears, explain how it works at the detail level you choose, and draw the flow of the code around it.