JpaRepository & CrudRepository
BeginnerJpaRepository extends PagingAndSortingRepository adding flush, batch-delete, and JPA-specific bulk operations on top of basic CRUD.
Overview
Spring Data JPA provides a repository abstraction that eliminates boilerplate DAO code. The hierarchy is: Repository → CrudRepository (basic CRUD) → PagingAndSortingRepository (pagination + sorting) → JpaRepository (adds flush, saveAllAndFlush, deleteAllInBatch, and findAll(Sort)). In practice, most Spring Boot applications extend JpaRepository which gives all capabilities. Spring Data generates the implementation at runtime — you write only the interface. Custom query methods are derived from method names, @Query annotations, or Specifications for dynamic queries.
Repository hierarchy and core methods
JpaRepository<T, ID> provides: save(entity), saveAll(entities), findById(id), findAll(), findAllById(ids), existsById(id), count(), delete(entity), deleteById(id), deleteAll(), findAll(Sort), findAll(Pageable), flush(), saveAllAndFlush(), deleteAllInBatch(). The T is the entity type and ID is the primary key type.
// Define repository — Spring generates the implementation
public interface OrderRepository extends JpaRepository<Order, Long> {
// All JpaRepository methods available immediately:
// save, findById, findAll, delete, count, existsById, ...
}
// Usage in service
@Service
@Transactional(readOnly = true)
public class OrderService {
private final OrderRepository orderRepository;
public Optional<Order> findById(Long id) {
return orderRepository.findById(id); // returns Optional — no NPE
}
public List<Order> findAll() {
return orderRepository.findAll();
}
@Transactional
public Order save(Order order) {
return orderRepository.save(order); // INSERT or UPDATE (merge)
}
@Transactional
public void delete(Long id) {
orderRepository.deleteById(id);
}
public long count() {
return orderRepository.count();
}
// Batch operations — much faster than N individual deletes
@Transactional
public void deleteAll(List<Order> orders) {
orderRepository.deleteAllInBatch(orders); // single DELETE ... WHERE id IN (...)
}
}Derived query methods by method name
Spring Data parses method names and generates JPQL automatically. Keywords: findBy, countBy, existsBy, deleteBy; conditions: And, Or, Between, LessThan, GreaterThan, Like, Containing, StartingWith, EndingWith, In, NotNull, IsNull, OrderBy. Method names can get verbose — use @Query for complex conditions.
public interface OrderRepository extends JpaRepository<Order, Long> {
// Simple field lookup
List<Order> findByStatus(OrderStatus status);
List<Order> findByCustomerId(Long customerId);
// Combined conditions
List<Order> findByStatusAndCreatedAtAfter(OrderStatus status, LocalDateTime date);
// Collection parameter
List<Order> findByStatusIn(List<OrderStatus> statuses);
// Null / not null
List<Order> findByShippedAtIsNull();
List<Order> findByShippedAtIsNotNull();
// Comparison
List<Order> findByTotalGreaterThan(BigDecimal amount);
List<Order> findByTotalBetween(BigDecimal min, BigDecimal max);
// Count / exists
long countByStatus(OrderStatus status);
boolean existsByCustomerIdAndStatus(Long customerId, OrderStatus status);
// Delete (requires @Transactional on calling method)
void deleteByCreatedAtBefore(LocalDateTime cutoff);
// Sorted
List<Order> findByStatusOrderByCreatedAtDesc(OrderStatus status);
// Pageable result
Page<Order> findByCustomerId(Long customerId, Pageable pageable);
}Projections and DTO queries
Fetching full entities when you only need a few fields wastes memory and serialisation overhead. Spring Data supports interface projections (closed projections map fields exactly) and class-based DTO projections (constructor expression). Projections only SELECT the needed columns, reducing database load.
// Interface projection — Spring generates a proxy
public interface OrderSummary {
Long getId();
OrderStatus getStatus();
BigDecimal getTotal();
// Only id, status, total are SELECTed — not all columns
}
// DTO projection — class with matching constructor
public record OrderDTO(Long id, String customerName, BigDecimal total) {}
public interface OrderRepository extends JpaRepository<Order, Long> {
// Interface projection
List<OrderSummary> findByStatus(OrderStatus status);
// DTO projection — JPQL constructor expression
@Query("SELECT new com.example.OrderDTO(o.id, c.name, o.total) " +
"FROM Order o JOIN o.customer c " +
"WHERE o.status = :status")
List<OrderDTO> findOrderDTOsByStatus(@Param("status") OrderStatus status);
// Dynamic projection — caller chooses what to project
<T> List<T> findByCustomerId(Long customerId, Class<T> type);
}
// Usage:
List<OrderSummary> summaries = repo.findByStatus(PENDING);
List<OrderDTO> dtos = repo.findByCustomerId(42L, OrderDTO.class);Key Points to Remember
- 1Extend JpaRepository<Entity, Id> — Spring generates the implementation at runtime with no code required
- 2save() performs INSERT for new entities (no id) and UPDATE (merge) for managed/detached entities
- 3deleteAllInBatch() issues a single DELETE ... WHERE id IN (...) — much faster than N individual deleteById calls
- 4Derived method names generate JPQL automatically; use @Query for complex conditions to keep method names short
- 5Always use readOnly = true on @Transactional for query-only methods — reduces overhead and enables Hibernate optimisations
- 6Interface projections SELECT only specified columns; DTO projections (constructor expression) work for multi-table queries
Interview Questions
Sign in to ask AriaWhat is the difference between CrudRepository, JpaRepository, and PagingAndSortingRepository?
What does save() do differently for a new entity vs a detached entity?
How would you fetch only id and status from an orders table without loading the full entity?
Why is deleteAllInBatch() preferred over calling deleteById() in a loop?
How does Spring Data generate SQL from method names like findByStatusAndCreatedAtAfter?
Ask Aria about JpaRepository & CrudRepository
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.