Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b7rnC7jppGGdHGESvy
3.7 KiB
soft-delete
Companion module for the ankurm.com article Soft Delete in Hibernate 7: @SoftDelete vs @SQLDelete + @SQLRestriction.
Every claim in the article comes from a test here; each test writes what it observed to output/. Tests that touch a
database run twice, against in-memory H2 and against a real PostgreSQL 17 container started by Testcontainers.
Versions
| Component | Version | Notes |
|---|---|---|
| Hibernate ORM | 7.4.14.Final |
newest 7.x GA on Maven Central on 2026-10-28 (8.0.0.Beta4 is also listed; it is not GA). Overrides the 7.4.5.Final that Spring Boot 4.1.1 manages |
| Spring Boot / Spring Data JPA | 4.1.1 / 4.1.1 |
parent POM; only the Spring Data test needs it |
| H2 | 2.4.240 |
in memory |
| PostgreSQL | 17.11 |
postgres:17-alpine through Testcontainers 2.0.5 (needs a running Docker daemon) |
| Java | 25 (Temurin 25.0.5+8) |
SoftDeleteType in the shipped jar: ACTIVE, DELETED, TIMESTAMP (see output/00-javap.txt). TIMESTAMP is absent from
6.6.0.Final and present in 7.0.0.Final (both jars inspected with javap).
Run
./scripts/run-all.sh # javap dump + mvn clean test; regenerates everything in output/
Mapping styles
| Class | Mapping |
|---|---|
ArticleDeleted |
@SoftDelete (default, boolean deleted) |
ArticleActive |
@SoftDelete(strategy = ACTIVE) |
ArticleStamped |
@SoftDelete(strategy = TIMESTAMP, columnName = "deleted_at") |
ArticleManual |
@SQLDelete + @SQLRestriction |
ArticleYesNo |
converter = YesNoConverter.class |
SoftBase / Note |
@SoftDelete on a @MappedSuperclass |
Author, Book, Tag, Reader, Shelf, Item |
associations, collection-level @SoftDelete, cascades |
ArticleRepository |
Spring Data JPA |
Output
| File | What it shows |
|---|---|
00-javap.txt |
javap of SoftDelete, SoftDeleteType, SQLDelete, SQLRestriction from the shipped jar |
01-api-shape.txt |
enum values, default column names and annotation defaults read by reflection |
02-ddl-and-sql.txt |
DDL and the SQL for persist / find / remove / count under all four styles; deleting an already-deleted row |
03-restore-and-native.txt |
native queries bypass the predicate, HQL cannot restore, stale persistence context, restore per strategy |
04-unique-constraints.txt |
six unique-index variants on H2 and PostgreSQL: plain, (email, deleted), partial, (email, deleted_at), NULLS NOT DISTINCT |
05-associations.txt |
deleted parent, new child of a deleted parent, collections, many-to-many (join rows are soft-deleted too), collection-level @SoftDelete, cascades, purging |
06-lazy-to-one.txt |
@ManyToOne(fetch = LAZY) towards a @SoftDelete entity is rejected at startup |
07-bulk-and-sqldelete.txt |
HQL bulk delete (soft vs real DELETE), stale double delete, converter, @MappedSuperclass |
08-spring-data.txt |
deleteById, findById, count, derived deletes, save of a soft-deleted instance, bulk and native @Query, deleteAll |
Not covered (not run)
@SoftDelete with inheritance hierarchies, second-level cache, Envers, SQL Server / Oracle / MySQL (filtered or function-based
indexes there are described in the article but were not executed), and Spring Data on PostgreSQL (the Spring test runs on H2).