The Repository Interface Hierarchy
Spring Data organizes repository capabilities into a layered hierarchy of interfaces, each adding more functionality: the marker interface Repository<T, ID> has no methods and simply flags a type for component scanning; CrudRepository<T, ID> adds basic create-read-update-delete methods like save, findById, findAll, and deleteById; PagingAndSortingRepository<T, ID> adds findAll(Pageable) and findAll(Sort) for paged and ordered results; and JpaRepository<T, ID>, the interface most Spring Boot developers extend directly, combines all of the above with JPA-specific extras like flush(), saveAndFlush(), and batch deletion via deleteAllInBatch().
Cricket analogy: This hierarchy is like the tiers of domestic cricket — club level (Repository, the base marker), state level (CrudRepository, basic operations), and international level (JpaRepository), each tier layering on more capability and structure than the one below.
Query Method Derivation
One of Spring Data JPA's signature features is deriving SQL from method names: declaring List<Book> findByAuthorLastNameAndStatus(String lastName, BookStatus status) on a repository interface causes Spring Data to parse the method name at startup, recognize the By keyword, split on And/Or, and generate the equivalent JPQL automatically — no method body required. Supported keywords extend beyond simple equality to include Containing, StartingWith, GreaterThan, Between, OrderBy, and IgnoreCase, letting a surprisingly large slice of everyday queries be expressed purely through interface method signatures.
Cricket analogy: Query derivation is like a scorer's shorthand notation where 'c Dhoni b Bumrah' unambiguously encodes 'caught by Dhoni, bowled by Bumrah' — a compact naming convention that expands into the full, precise meaning automatically.
Custom Query Methods with @Query
When a query is too complex to express through derived method names — or when the derived name would become unreadably long — you annotate a repository method with @Query and supply JPQL (or, with nativeQuery = true, raw SQL) directly. Named parameters are bound with :paramName in the query string and matched to method arguments annotated @Param("paramName"), which keeps the binding explicit and refactor-safe compared to relying purely on positional parameters.
Cricket analogy: @Query is like a captain calling a specific, unconventional field placement by hand rather than relying on the standard pre-set fields — sometimes the situation demands an exact, custom instruction instead of the default convention.
public interface BookRepository extends JpaRepository<Book, Long> {
// Derived query — no implementation needed
List<Book> findByAuthorLastNameAndStatus(String lastName, BookStatus status);
List<Book> findByTitleContainingIgnoreCaseOrderByTitleAsc(String keyword);
// Custom JPQL query with named parameters
@Query("SELECT b FROM Book b WHERE b.author.id = :authorId AND b.publishedYear >= :sinceYear")
List<Book> findRecentBooksByAuthor(@Param("authorId") Long authorId,
@Param("sinceYear") int sinceYear);
// Native SQL query, useful for database-specific features
@Query(value = "SELECT * FROM books WHERE to_tsvector('english', title) @@ plainto_tsquery(:term)",
nativeQuery = true)
List<Book> fullTextSearch(@Param("term") String term);
// Paging support inherited from PagingAndSortingRepository via JpaRepository
Page<Book> findByStatus(BookStatus status, Pageable pageable);
}Custom Repository Implementations
For logic that neither derived methods nor @Query can express cleanly — dynamic queries built with the JPA Criteria API, integration with a search engine, or complex multi-step logic — Spring Data supports fragment interfaces: you declare an interface like BookRepositoryCustom with the method signature, provide an implementation class named BookRepositoryCustomImpl (the Impl suffix is the default convention Spring Data looks for), and have your main BookRepository extend both JpaRepository and BookRepositoryCustom. Spring Data then composes all three into a single proxy at runtime, so callers just see one cohesive BookRepository interface.
Cricket analogy: Composing a custom fragment into a repository is like a franchise signing a specialist death-bowling coach who only handles the last four overs, while the rest of the coaching staff runs everything else — together they form one seamless team.
Spring Data repository proxies are created lazily by the Spring Data JPA infrastructure through @EnableJpaRepositories (auto-enabled by Spring Boot's auto-configuration), which scans for interfaces extending Repository and generates a SimpleJpaRepository-backed proxy for each — you never implement JpaRepository's inherited methods yourself.
Derived query method names can become unwieldy and error-prone once you chain more than two or three conditions — a name like findByStatusAndAuthorLastNameAndPublishedYearGreaterThanAndTitleContainingIgnoreCase is hard to read and easy to typo. Switch to @Query with JPQL once a derived method name stops being self-explanatory at a glance.
- Repository, CrudRepository, PagingAndSortingRepository, and JpaRepository form a layered hierarchy of increasing capability.
- Method-name query derivation parses keywords like And, Or, Containing, and OrderBy to generate JPQL automatically.
- @Query lets you supply explicit JPQL or, with nativeQuery = true, raw SQL for cases derivation can't express.
- @Param binds named parameters (:paramName) to method arguments, which is safer and more refactor-friendly than positional binding.
- Custom fragment interfaces (suffixed Impl) let you inject Criteria API or other bespoke logic into a composed repository proxy.
- Spring Boot auto-enables repository scanning, generating SimpleJpaRepository-backed proxies without manual configuration.
- Switch from derived method names to explicit @Query once the method name becomes long or hard to read at a glance.
Practice what you learned
1. Which interface in the Spring Data hierarchy first introduces basic save/find/delete methods?
2. What does the method name findByTitleContainingIgnoreCase generate?
3. How do you bind a named parameter in a @Query JPQL string to a method argument?
4. What naming convention does Spring Data expect for a custom repository fragment implementation?
5. When should you prefer @Query with JPQL over a derived query method name?
Was this page helpful?
You May Also Like
Spring Data JPA Basics
Learn how Spring Data JPA layers convention-driven repositories and auto-configuration on top of JPA and Hibernate to remove persistence boilerplate.
Defining JPA Entities
Learn how to model database tables as Java classes using JPA annotations, covering identifiers, column mapping, and entity relationships.
Querying with JPA
Go beyond basic repository methods to master JPQL, the Criteria API, pagination, and performance pitfalls like the N+1 select problem.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics