2026-10-23 · Q&A guide

Use Generics to Reduce Boilerplate in Spring Data JPA Repositories

Learn how to use generics in Spring Data JPA repositories to eliminate repetitive CRUD interfaces and services, streamlining your data access layer.

Why Generics Save Boilerplate

When every entity gets its own repository and service, the codebase grows quickly. If all entities share the same CRUD behaviour, the duplication is unnecessary. Generics let you write a single repository and service that work with any entity type and its primary key, keeping the codebase small and maintainable.

Generic Repository Interface

Define a repository that extends JpaRepository with type parameters for the entity and its ID. No extra methods are required for basic CRUD.

public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {}

Generic Service Layer

Create a generic service interface and implementation that delegate to the repository. The implementation receives the correct repository bean via constructor injection.

public interface GenericService<T, ID extends Serializable> {
    T save(T entity);
    Optional<T> findById(ID id);
    List<T> findAll();
    void deleteById(ID id);
}

@Service
public class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> {
    private final GenericRepository<T, ID> repository;

    public GenericServiceImpl(GenericRepository<T, ID> repository) {
        this.repository = repository;
    }

    @Override
    public T save(T entity) {
        return repository.save(entity);
    }
    // delegate other methods similarly
}

Putting It All Together

Wire the generic service for a concrete entity by declaring a bean. Spring Data automatically creates a repository bean for each entity, so you can inject GenericService<Facility, Long> wherever needed.

@Bean
public GenericService<Facility, Long> facilityService(GenericRepository<Facility, Long> repo) {
    return new GenericServiceImpl<>(repo);
}

@RestController
@RequestMapping("/facilities")
public class FacilityController {
    private final GenericService<Facility, Long> service;

    public FacilityController(GenericService<Facility, Long> service) {
        this.service = service;
    }

    @PostMapping
    public Facility create(@RequestBody Facility facility) {
        return service.save(facility);
    }
}

Custom Queries and Advanced Use Cases

The generic repository provides only CRUD. If an entity needs custom queries, create a dedicated repository interface that extends GenericRepository. That interface can declare query methods or @Query annotations while still benefiting from the generic service.

Takeaway: Generics let you centralize CRUD logic in Spring Data JPA, cutting boilerplate without sacrificing type safety.

People also ask

Can I add custom query methods on a generic repository?

No, the generic repository only supplies CRUD. For custom queries, create a concrete repository that extends GenericRepository.

Does this approach affect Spring Data's query derivation?

Query derivation works on concrete repository interfaces. Use a specific interface for any derived query you need.

Inspired by a public discussion on Stack Overflow. This article is an original explanation for learners.

← All posts