3.1 KiB
3.1 KiB
sdjpa4-demo
Runnable companion project for the Spring Data JPA 3 to 4 migration series on ankurm.com:
- Spring Data JPA 3 to 4 Migration Guide (overview, start here)
- Migration Baseline
- Query Engine Rewrite: Criteria to JPQL
- Advanced Migration: Specifications, AOT, EntityManager
Four entities, three repositories, a @NullMarked package, 20 passing behavior tests, and an A-L demo runner that prints every labelled console output shown in the articles. Boots H2 in memory, so there's nothing to install beyond a JDK.
Built and verified on Eclipse Temurin JDK 21, Spring Boot 4.0.6, Spring Data JPA 4.0.6, Hibernate ORM 7.2, H2 in-memory.
Run it
git clone https://ankurm.com/git.app/asmhatre/sdjpa4-demo.git
cd sdjpa4-demo
./mvnw spring-boot:run # prints the labelled A-L demonstrations
./mvnw test # runs the 20 behavior tests (all green)
Tags
article-1-baseline,article-2-query-engine,article-3-advanced- the exact code each article quotes, frozen at publish time.corner-scenarios(andmain) - the enriched, current state described below. Some method signatures have moved on from the article-tagged snapshots (e.g.Book'spriceis now an embeddedMoneyvalue object, not a bareBigDecimal), 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):PredicateSpecificationreused across a read and a bulk delete, an explicitDeleteSpecification(CriteriaDelete-backed), and anUpdateSpecification(CriteriaUpdate-backed bulk update composed from anUpdateOperation+ awhere(...)predicate). JpaSort.unsafe(...)with aCASEexpression - a realORDER BY case when country = 'US' then 0 else 1 endsort combined with a plain derived query.Money, an@Embeddablerecord value object onBook.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 resolveamountagainst the nestedprice.amountpath via a plainfindBy...derived method - it throwsPropertyReferenceException: No property 'amount' found for type 'Book'. The fix is an explicit@Queryconstructor expression (select new ...BookSummary(b.title, b.price.amount) from Book b where ...), which does work for nested/embedded paths. SeeBookRepository.findByPriceAmountLessThanEqual.
All of the above is exercised by both the demo runner (DemoRunner.java, sections H-L) and dedicated tests in MigrationBehaviorTests.java.