Add DemoRunner.java
This commit is contained in:
112
src/main/java/com/ankurm/sdjpa4demo/DemoRunner.java
Normal file
112
src/main/java/com/ankurm/sdjpa4demo/DemoRunner.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.ankurm.sdjpa4demo;
|
||||
|
||||
import com.ankurm.sdjpa4demo.domain.Author;
|
||||
import com.ankurm.sdjpa4demo.domain.Book;
|
||||
import com.ankurm.sdjpa4demo.repo.AuthorRepository;
|
||||
import com.ankurm.sdjpa4demo.repo.BookRepository;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@Profile("!test")
|
||||
public class DemoRunner implements CommandLineRunner {
|
||||
|
||||
private final AuthorRepository authors;
|
||||
private final BookRepository books;
|
||||
|
||||
public DemoRunner(AuthorRepository authors, BookRepository books) {
|
||||
this.authors = authors;
|
||||
this.books = books;
|
||||
}
|
||||
|
||||
private static void section(String title) {
|
||||
System.out.println("\n================ " + title + " ================");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(String... args) {
|
||||
Author gof = authors.save(new Author("Erich Gamma", "CH"));
|
||||
Author bloch = authors.save(new Author("Joshua Bloch", "US"));
|
||||
Author anon = authors.save(new Author("Anonymous", null)); // null country
|
||||
authors.save(new Author("Martin Fowler", "UK"));
|
||||
authors.save(new Author("Kent Beck", "DE")); // bookless: safe to delete in section G
|
||||
|
||||
books.save(new Book("Design Patterns", new BigDecimal("42.00"), gof));
|
||||
books.save(new Book("Effective Java", new BigDecimal("45.00"), bloch));
|
||||
books.save(new Book("Refactoring", new BigDecimal("45.00"), anon)); // duplicate price 45.00
|
||||
|
||||
// (A) Optional vs non-null vs @Nullable single-result semantics
|
||||
section("A. Null-handling of single-result query methods");
|
||||
Optional<Author> opt = authors.findByName("No Such Author");
|
||||
System.out.println("findByName(Optional) missing -> " + opt);
|
||||
|
||||
try {
|
||||
authors.getByName("No Such Author");
|
||||
} catch (EmptyResultDataAccessException ex) {
|
||||
System.out.println("getByName(non-null) missing -> throws " + ex.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
Author byCountry = authors.findByCountry("ZZ");
|
||||
System.out.println("findByCountry(@Nullable) missing -> " + byCountry);
|
||||
|
||||
// (B) JSpecify @NullMarked rejects null arguments at runtime
|
||||
section("B. JSpecify @NullMarked parameter enforcement");
|
||||
try {
|
||||
authors.getByName(null);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.out.println("getByName(null) -> throws " + ex.getClass().getSimpleName()
|
||||
+ ": " + ex.getMessage());
|
||||
}
|
||||
System.out.println("findByCountry(null) is allowed (@Nullable param) -> "
|
||||
+ authors.findByCountry(null));
|
||||
|
||||
// (C) NULLS precedence in Sort (consistent in SD JPA 4)
|
||||
section("C. NULLS FIRST vs NULLS LAST in Sort");
|
||||
List<Author> nullsFirst = authors.findByNameStartingWith(
|
||||
"", Sort.by(Sort.Order.asc("country").nullsFirst()));
|
||||
System.out.println("country ASC NULLS FIRST -> "
|
||||
+ nullsFirst.stream().map(Author::getCountry).toList());
|
||||
List<Author> nullsLast = authors.findByNameStartingWith(
|
||||
"", Sort.by(Sort.Order.asc("country").nullsLast()));
|
||||
System.out.println("country ASC NULLS LAST -> "
|
||||
+ nullsLast.stream().map(Author::getCountry).toList());
|
||||
|
||||
// (D) Derived query now flows through JPQL (see generated SQL just above in log)
|
||||
section("D. Derived query -> JPQL path");
|
||||
System.out.println("findByNameStartingWith('J') -> "
|
||||
+ authors.findByNameStartingWithOrderByNameAsc("J")
|
||||
.stream().map(Author::getName).toList());
|
||||
|
||||
// (E) New JPA 3.2 JPQL: || concatenation + replace()
|
||||
section("E. New JPQL functions ( || and replace() )");
|
||||
System.out.println("badgeFor(gof) -> " + authors.badgeFor(gof.getId()));
|
||||
System.out.println("badgeFor(anon) -> " + authors.badgeFor(anon.getId()));
|
||||
|
||||
// (F) Single-result that matches multiple rows -> non-unique edge case
|
||||
section("F. Non-unique single result edge case");
|
||||
try {
|
||||
Book b = books.findByPrice(new BigDecimal("45.00"));
|
||||
System.out.println("findByPrice(45.00) -> " + b);
|
||||
} catch (IncorrectResultSizeDataAccessException ex) {
|
||||
System.out.println("findByPrice(45.00) matches 2 rows -> throws "
|
||||
+ ex.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
// (G) Derived DELETE returns affected count
|
||||
section("G. Derived delete returns count");
|
||||
long deleted = authors.deleteByCountry("DE");
|
||||
System.out.println("deleteByCountry('DE') removed -> " + deleted + " row(s)");
|
||||
|
||||
section("DONE");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user