skip to content

Repository Interface Hierarchy

Repository is a marker, CrudRepository adds the basics, and the paging and list variants build on it, with @NoRepositoryBean marking base interfaces that must not be instantiated. Interviewers ask which interface you would extend and why the smallest one is often right.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is the Repository marker interface in Spring Data, and how does CrudRepository relate to it?

level: juniorimportance: must knowfreq 70%

answer

  1. Repository = empty marker + type capture
  2. T = entity, ID = key
  3. CrudRepository adds save/find/delete
  4. findAll returns Iterable not List
  5. proxy backed by SimpleJpaRepository

basics

~20 s

Repository<T, ID> is an empty marker interface that tells Spring Data an interface is a repository and captures the entity type T and its id type ID. CrudRepository extends it and adds ready-made save/find/delete methods.

solid answer

~40 s

Repository<T, ID> is the root of Spring Data's repository hierarchy. It declares no methods — it is a pure marker (technically a generic-typing) interface. Its only job is to (a) flag an interface so Spring Data's classpath scanning creates a proxy implementation for it at runtime, and (b) capture two type parameters: T, the domain/entity type, and ID, its identifier type. You rarely extend Repository directly. Instead you extend CrudRepository<T, ID>, which builds on Repository and supplies generic CRUD operations: save, saveAll, findById, existsById, findAll, findAllById, count, deleteById, delete, deleteAll. You write no implementation — Spring Data generates the proxy backed by a store-specific base class (e.g. SimpleJpaRepository). This is the whole point of Spring Data: declare an interface, get an implementation for free.

code

java · 15 lines
java
public interface UserRepository extends CrudRepository<User, Long> {
    // Inherited for free: save, findById, findAll, deleteById, count, ...
    // Plus a derived query — implemented from the method name:
    Optional<User> findByEmail(String email);
}

// Usage — no implementation class written anywhere:
@Service
class UserService {
    private final UserRepository repo;
    UserService(UserRepository repo) { this.repo = repo; }

    User register(User u) { return repo.save(u); }
    Iterable<User> all()  { return repo.findAll(); } // Iterable, not List
}

go deeper

for a junior

Know Repository is the empty marker, CrudRepository gives CRUD for free, and you only declare an interface.

for a middle

Explain type capture (T/ID), that findAll returns Iterable, and that a store-specific proxy provides the implementation.

for a senior

Discuss extending Repository directly for minimal surface, @NoRepositoryBean on the base interfaces, and Commons vs JPA module split.

for a principal

Reason about interface-surface design as API governance — exposing only the operations a repository should offer rather than defaulting to broad CRUD.

## The hierarchy Spring Data organizes its repository interfaces as a layered hierarchy so you can pick exactly the capabilities you want. **`Repository<T, ID>`** is the root. It is a *marker interface* — an interface with **no methods at all**. Its two responsibilities: 1. **Discovery**: When Spring Data scans the classpath (triggered by `@EnableJpaRepositories`, Spring Boot auto-config, etc.), any interface that (transitively) extends `Repository` is picked up, and a **dynamic proxy** implementing it is created and registered as a Spring bean. An interface that does *not* extend `Repository` (and is not annotated `@RepositoryDefinition`) is invisible to Spring Data. 2. **Type capture**: The generics `T` (the domain/entity type, e.g. `User`) and `ID` (the id type, e.g. `Long`) tell Spring Data what entity this repository manages and what its primary-key type is. Query derivation, `findById`, etc. all rely on these. **`CrudRepository<T, ID>`** `extends Repository<T, ID>` and declares the generic CRUD methods: - `<S extends T> S save(S entity)` and `saveAll(Iterable)` - `Optional<T> findById(ID id)`, `boolean existsById(ID id)` - `Iterable<T> findAll()`, `Iterable<T> findAllById(Iterable<ID> ids)` - `long count()` - `void deleteById(ID id)`, `void delete(T entity)`, `void deleteAllById(...)`, `void deleteAll(...)`, `void deleteAll()` Note `findAll()` returns **`Iterable<T>`**, not `List<T>` — a deliberate lowest-common-denominator choice (some stores stream). Spring Data 3.0 added `ListCrudRepository` if you want `List` back. ## Where the implementation comes from You never write an `implements` class. At runtime Spring Data creates a proxy whose CRUD methods are backed by a **store-specific base implementation**: `SimpleJpaRepository` for JPA, `SimpleMongoRepository` for MongoDB, etc. Derived query methods (`findByEmail`) are parsed from the method name and implemented on the fly. ## Gotchas - `Repository` being empty means extending it alone gives you **zero** methods — you'd add only derived/`@Query` methods. That's a valid, minimal-surface choice for read-only or narrowly-scoped repositories. - The interface itself is annotated `@NoRepositoryBean` so Spring never tries to instantiate `Repository`/`CrudRepository` directly (see the related question). - `CrudRepository` is store-agnostic — it lives in `org.springframework.data.repository` (Spring Data Commons), not in the JPA module. `JpaRepository` is the JPA-specific extension.

  • Why does findAll() return Iterable<T> rather than List<T>?
    Iterable is the lowest common denominator across stores — some can stream results without materializing a full list. Spring Data 3.0 added ListCrudRepository whose findAll returns List for convenience.
  • If you extend Repository directly instead of CrudRepository, what methods do you get?
    None — Repository is empty. You'd only get the derived/@Query methods you declare yourself, which is useful to expose a minimal, controlled API surface.

saying these in an interview costs you the question

  • Claiming Repository has save/find/delete methods (it's empty)
  • Saying findAll returns List<T>
  • Thinking you must write an implements class for CrudRepository

context

open as a page

What is @NoRepositoryBean and when must you use it?

level: seniorimportance: must knowfreq 50%

basics

~20 s

@NoRepositoryBean marks a repository interface as a base/intermediate interface that Spring Data should NOT create a bean/proxy for. You put it on shared base interfaces you extend but never use directly, so Spring doesn't try to instantiate them.

open as a page

What do ListCrudRepository and ListPagingAndSortingRepository add over their base interfaces, and why were they introduced?

level: middleimportance: should knowfreq 45%

basics

~10 s

They were added in Spring Data 3.0. ListCrudRepository is like CrudRepository but findAll, findAllById and saveAll return List<T> instead of Iterable<T>. ListPagingAndSortingRepository makes findAll(Sort) return List<T>.

open as a page

Explain PagingAndSortingRepository. What changed about its position in the hierarchy in Spring Data 3.0?

level: seniorimportance: should knowfreq 40%

basics

~10 s

PagingAndSortingRepository<T, ID> adds two methods: findAll(Sort) for sorted results and findAll(Pageable) returning a Page<T> for paginated results. In Spring Data 3.0 it stopped extending CrudRepository, so it no longer inherits save/delete.

open as a page

You're setting a team convention for which repository interface to extend. Walk through the trade-offs across the hierarchy from Repository up to JpaRepository.

level: principalimportance: should knowfreq 25%

basics

~20 s

Extend the narrowest interface that exposes only the operations a repository should offer: Repository for a minimal custom surface, CrudRepository/ListCrudRepository for CRUD, PagingAndSortingRepository for read/paging, JpaRepository when you need JPA extras like flush and batch. Broader interfaces mean broader, harder-to-govern APIs.

open as a page