What is Spring Data JPA, and how do repository interfaces like JpaRepository work without an implementation?
Quick Answer
Spring Data JPA is a layer on top of JPA/Hibernate that eliminates most boilerplate data-access code: you declare a repository as a plain interface extending JpaRepository<Entity, IdType>, and Spring Data generates a working implementation at runtime via a dynamic proxy, backed by a SimpleJpaRepository instance that implements the common CRUD operations using the underlying JPA EntityManager. You never write an implementation class yourself — Spring Data creates one automatically when the ApplicationContext starts.
Detailed Answer
Ordinarily, implementing data access with plain JPA means writing a class that injects an EntityManager and hand-writes methods like findById, save, deleteById — mostly repetitive boilerplate that's nearly identical across every entity type.
Spring Data JPA eliminates that boilerplate: you declare a repository as a plain interface, extending a base interface like JpaRepository<Entity, IdType>, with no implementation class at all:
public interface OrderRepository extends JpaRepository<Order, Long> {
// no implementation needed — save(), findById(), findAll(), deleteById(), etc. all work already
}
@Service
class OrderService {
private final OrderRepository repository; // just inject the interface directly
OrderService(OrderRepository repository) { this.repository = repository; }
Order getOrder(Long id) {
return repository.findById(id).orElseThrow();
}
}
How this actually works under the hood: when the ApplicationContext starts, Spring Data JPA's infrastructure scans for interfaces extending its repository marker interfaces (enabled via @EnableJpaRepositories, which Spring Boot's auto-configuration wires up automatically once spring-boot-starter-data-jpa is present). For each one found, it creates a dynamic proxy implementing that interface at runtime — the proxy's invocation handler delegates standard CRUD method calls to SimpleJpaRepository, a single, generic, concrete implementation class provided by Spring Data that performs the actual work via the underlying JPA EntityManager, parameterized by the specific entity/ID types your interface declared.
This is exactly the same underlying idea as Spring AOP's proxy mechanism (a runtime-generated implementation standing in for an interface) applied specifically to eliminate repetitive data-access boilerplate — JpaRepository already gives you save, findById, findAll, deleteById, count, and pagination/sorting support, all without a single line of implementation code from you.