Spring Data JPA — JpaRepository
BeginnerExtend JpaRepository<Entity, ID> to get findById, findAll, save, delete, and pagination out of the box; Spring Data generates the implementation at runtime.
Overview
Spring Data JPA eliminates boilerplate repository code by generating implementations at runtime from interface definitions. Extending `JpaRepository<T, ID>` gives you a full CRUD API plus batch operations and sorting/pagination via `PagingAndSortingRepository`. Spring Data parses **derived query method names** (`findByEmailAndStatus`) into JPQL without any implementation code. For complex queries, `@Query` accepts JPQL or native SQL. `@Modifying` + `@Transactional` turns a query into a bulk UPDATE/DELETE. Projections (interface-based or DTO constructor) reduce the columns fetched from the database.
JpaRepository Basics & Derived Queries
Spring Data inspects the method name, parses the entity's fields, and generates the corresponding JPQL at application startup. Common keywords: `findBy`, `countBy`, `existsBy`, `deleteBy`; connectors: `And`, `Or`; constraints: `Containing`, `StartingWith`, `Between`, `LessThan`, `OrderBy`. No SQL or implementation code required.
@Entity
public class User {
@Id @GeneratedValue Long id;
String email;
String status;
int age;
}
public interface UserRepository extends JpaRepository<User, Long> {
// SELECT u FROM User u WHERE u.email = ?1
Optional<User> findByEmail(String email);
// SELECT u FROM User u WHERE u.status = ?1 AND u.age >= ?2
List<User> findByStatusAndAgeGreaterThanEqual(String status, int minAge);
// SELECT COUNT(u) FROM User u WHERE u.status = ?1
long countByStatus(String status);
// SELECT u FROM User u WHERE u.email LIKE %?1%
List<User> findByEmailContaining(String fragment);
// Pagination: returns a Page<User> with total count
Page<User> findByStatus(String status, Pageable pageable);
}
// Usage
Page<User> page = userRepo.findByStatus("ACTIVE",
PageRequest.of(0, 20, Sort.by("email")));@Query, @Modifying, and Native Queries
When derived query names become unwieldy, use `@Query` with JPQL (entity names, not table names). Add `nativeQuery = true` for SQL when you need database-specific features. `@Modifying` is required for UPDATE/DELETE queries; always pair it with `@Transactional`.
public interface UserRepository extends JpaRepository<User, Long> {
// JPQL — entity fields, not column names
@Query("SELECT u FROM User u WHERE u.email = :email AND u.status = 'ACTIVE'")
Optional<User> findActiveByEmail(@Param("email") String email);
// Native SQL — table/column names
@Query(value = "SELECT * FROM users WHERE created_at > :since LIMIT :n",
nativeQuery = true)
List<User> findRecentNative(@Param("since") LocalDateTime since,
@Param("n") int n);
// Bulk update — must be @Modifying + @Transactional
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status WHERE u.age < :age")
int deactivateByAge(@Param("age") int age, @Param("status") String status);
}Projections — Reduce Fetched Columns
Fetching full entities when you only need two columns wastes memory and increases query time. Spring Data supports **interface projections** (proxy-based) and **DTO projections** (constructor expression). Interface projections are declarative; DTO projections give you a real class with business logic.
// Interface projection — Spring creates a proxy at runtime
public interface UserSummary {
String getEmail();
String getStatus();
}
List<UserSummary> findSummaryByStatus(String status);
// DTO projection — use SELECT new in @Query
public record UserDto(String email, int age) {}
@Query("SELECT new com.example.UserDto(u.email, u.age) FROM User u WHERE u.status = :s")
List<UserDto> findDtoByStatus(@Param("s") String status);
// Dynamic projection — caller chooses the type
<T> List<T> findByStatus(String status, Class<T> type);
// Usage:
List<UserSummary> summaries = repo.findByStatus("ACTIVE", UserSummary.class);
List<User> full = repo.findByStatus("ACTIVE", User.class);Key Points to Remember
- 1JpaRepository provides CRUD, batch, and pagination APIs without any implementation code
- 2Derived query method names are parsed into JPQL at startup — no SQL needed for simple queries
- 3@Query accepts JPQL (entity/field names) or native SQL (nativeQuery=true)
- 4@Modifying + @Transactional is required for UPDATE/DELETE @Query methods
- 5Interface projections reduce fetched columns without writing DTO constructors
- 6Pagination: return Page<T> and pass a Pageable parameter for count + data in one query
Interview Questions
Sign in to ask AriaWhen would you choose @Query over a derived query method name?
What is the difference between interface projections and DTO projections in Spring Data JPA?
Why does a @Modifying query also require @Transactional?
What is the difference between findById() returning Optional and getOne()/getReferenceById() returning a proxy?
How does Spring Data JPA validate derived query method names?
Ask Aria about Spring Data JPA — JpaRepository
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.