# sdjpa4-demo Companion code for the Spring Data JPA and transaction articles on **[ankurm.com](https://ankurm.com)**. Two independent Maven modules under one aggregator: | Module | Article(s) | Boot / JDK | |---|---|---| | [`migration-behavior/`](migration-behavior) | the four Spring Data JPA 3→4 migration articles | 4.0.6 / 21 | | [`transactions/`](transactions) | [@Transactional: propagation, isolation and the six silent failures](https://ankurm.com/) | 4.1.1 / 25 | The modules deliberately pin different Spring Boot versions. `migration-behavior` stays on 4.0.6 because that is what the four published migration articles were written and verified against, and upgrading it would silently invalidate output those articles quote. > **Moved in September 2026.** The migration project used to live at the repository root. It is > now under `migration-behavior/`; source paths gained that prefix and nothing else changed. The > `article-1-baseline`, `article-2-query-engine` and `article-3-advanced` tags still point at the > original layout, so a link into a tagged tree is unaffected. ```bash ./mvnw -DskipTests package # both modules ./mvnw test # every test in both ``` ## Tags - `article-1-baseline`, `article-2-query-engine`, `article-3-advanced` - the exact code each article quotes, frozen at publish time. - `corner-scenarios` (and `main`) - the enriched, current state described below. Some method signatures have moved on from the article-tagged snapshots (e.g. `Book`'s `price` is now an embedded `Money` value object, not a bare `BigDecimal`), so check out the matching article tag if you want the code to line up exactly with what's quoted in a given post. ## What's covered beyond the three articles (corner-scenario enrichment) - **Refined Specification API** (`AuthorSpecifications.java`): `PredicateSpecification` reused across a read and a bulk delete, an explicit `DeleteSpecification` (`CriteriaDelete`-backed), and an `UpdateSpecification` (`CriteriaUpdate`-backed bulk update composed from an `UpdateOperation` + a `where(...)` predicate). - **`JpaSort.unsafe(...)` with a `CASE` expression** - a real `ORDER BY case when country = 'US' then 0 else 1 end` sort combined with a plain derived query. - **`Money`, an `@Embeddable` record value object** on `Book.price`, with derived queries that traverse the embedded path (`findByPriceAmountGreaterThanEqual`, `findByPriceAmount`). - **A genuine corner case, found by actually running it**: a derived-query class-based (record) projection resolves constructor-parameter names against *direct* entity properties only. `BookSummary(String title, BigDecimal amount)` does **not** resolve `amount` against the nested `price.amount` path via a plain `findBy...` derived method - it throws `PropertyReferenceException: No property 'amount' found for type 'Book'`. The fix is an explicit `@Query` constructor expression (`select new ...BookSummary(b.title, b.price.amount) from Book b where ...`), which does work for nested/embedded paths. See `BookRepository.findByPriceAmountLessThanEqual`. All of the above is exercised by both the demo runner (`DemoRunner.java`, sections H-L) and dedicated tests in `MigrationBehaviorTests.java`.