Repositories

Master the Spring Data repository pattern — from basic JpaRepository to custom implementations, projections, and the repository design best practices for enterprise applications.

Last reviewed: January 2025

·Spring Boot 2.7Spring Boot 3.x
✅
Quick Understanding: Repositories are the data access layer in Spring. Extend JpaRepository<Entity, ID> and you get full CRUD. Add method names like findByEmail() and Spring generates the query. For complex queries, use @Query. For very complex logic, add a custom implementation. The repository pattern keeps data access separate from business logic.

🔍 What Is It?

The repository pattern in Spring Data provides a clean abstraction over data access.

Repository hierarchy:

  • →Repository<T, ID> — marker interface
  • →CrudRepository<T, ID> — basic CRUD
  • →PagingAndSortingRepository<T, ID> — + pagination/sorting
  • →JpaRepository<T, ID> — + JPA-specific methods (flush, batch)
  • →JpaSpecificationExecutor<T> — + dynamic Specification queries

Query methods:

  • →Derived: findByFirstNameAndLastName(String first, String last)
  • →@Query JPQL: @Query("SELECT u FROM User u WHERE u.email = :email")
  • →@Query native: @Query(value="SELECT * FROM users WHERE email=:email", nativeQuery=true)
  • →Custom implementation: full control with EntityManager

Projections:

  • →Interface projections: define an interface with getters for needed fields
  • →DTO projections: @Query with new constructor expression
  • →Dynamic projections: repository method returns <T> with type parameter

🎯 Why Do We Need It?

Repositories provide a clean boundary between business logic and data access. Services work with domain objects and call repositories. Repositories handle SQL, transactions, and ORM mapping. This separation enables:

  • →Unit testing services with mock repositories
  • →Swapping databases without changing services
  • →Centralizing query logic in one place

🏗️ What Happens in Real Projects?

In a production app, repositories are the only place that touches the database (via JPA). Services call repositories, not EntityManager directly. This keeps the architecture clean and testable.

⚙️ How It Works

Spring Data creates JDK proxies for repository interfaces at startup. Method calls are intercepted and routed to the appropriate query strategy: derived query parser, @Query executor, or custom implementation delegate.

💻 Example

Repository Patterns — Projections, custom implementations, and auditing repositories.

java
// Only fetch id, name, email — not the full User entity
public interface UserSummary {
    Long getId();
    String getFirstName();
    String getLastName();
    String getEmail();

    // Computed property via SpEL
    @Value("#{target.firstName + ' ' + target.lastName}")
    String getFullName();
}

public interface UserRepository extends JpaRepository<User, Long> {

    // Returns projection instead of full entity
    List<UserSummary> findByDepartmentId(Long departmentId);

    // Dynamic projection — caller decides what to fetch
    <T> List<T> findByRole(UserRole role, Class<T> type);
}

// Usage:
List<UserSummary> summaries = userRepository.findByDepartmentId(deptId);
// SELECT id, first_name, last_name, email FROM users WHERE department_id=?
// NOT SELECT * — much faster for large entities

🤔 Common Confusions

❓ Should I put @Transactional on repository methods?

Spring Data JPA repositories are already @Transactional by default for write operations. Read operations are @Transactional(readOnly=true). You don't need to add @Transactional to repository methods. Add @Transactional to SERVICE methods when you need to combine multiple repository calls in one transaction.

❓ When should I use a custom implementation vs @Query?

Use @Query for static JPQL/SQL queries. Use custom implementation for: dynamic queries built at runtime, bulk operations with EntityManager, queries requiring complex logic, or when you need full control over the query execution.

🚨 Common Production Issues

Returning entities from repositories to controllers — always map to DTOs in the service layer

Large @Query annotations becoming maintenance nightmares — move complex queries to named queries or custom impl

Repository methods not in transactions when called from non-transactional context

✅ Best Practices

Use projections to fetch only needed fields — avoid loading full entities for list views

Add @Transactional(readOnly=true) to service methods that only read data

Keep repositories focused on data access — no business logic

Use custom implementations for complex dynamic queries instead of string concatenation

🏛️ Architect's Note

Senior Architect's Perspective

Repositories are the anti-corruption layer between your domain model and the database. Services should work with domain objects; repositories translate between domain objects and database rows. Never leak database concerns (column names, SQL syntax) into the service layer. If you find yourself writing SQL in a service, that query belongs in a repository.

💬 Frequently Asked Questions

Can I have multiple repositories for the same entity?

Yes, but it's unusual. A common pattern is having a primary UserRepository and a read-only UserReadRepository (extending Repository instead of JpaRepository) for the read model. This enforces that read operations don't accidentally trigger writes.

🎯 Key Takeaways

1JpaRepository provides full CRUD + pagination + sorting for free
2Derived query methods generate SQL from method names
3Interface projections fetch only needed columns — much faster for large entities
4Custom implementations give full EntityManager control for complex queries
5Spring Data repositories are already @Transactional — add @Transactional to service methods
6Keep repositories focused on data access only

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Spring Boot learning path

Browse the full Spring Boot topic collection and learning order on SplashRide.

Spring Boot Interview Prep

Practice Spring Boot interview questions and scenario-based answers.

Backend Engineer Career Path

A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Spring Data JPA

Master Spring Data JPA — the abstraction layer over Hibernate that eliminates boilerplate data access code through repository interfaces and derived query methods.

Entity Mapping

Master JPA entity mapping - @Entity, @Table, relationships, fetch strategy, and the patterns that prevent common ORM performance pitfalls.