Spring Data JPA Repositories, Queries, Pagination is an important Spring topic because it appears in real projects, debugging sessions, and interviews. Learn the meaning first, then connect it to a small working example so the rule does not stay abstract.
For this page, focus on what problem Spring Data JPA Repositories, Queries, Pagination solves, where developers usually make mistakes, and how to verify the result. The audit note for this lesson was: under 650 content words; limited checklist/practice/mistake/FAQ notes .
A strong understanding of Spring Data JPA Repositories, Queries, Pagination should include syntax, behavior, one realistic use case, one failure case, and one quick way to check your work with tools or output.
Spring Data JPA Repositories Queries Pagination should be studied as a practical Spring lesson, not as a label. Start by naming the input, the rule that changes the input, and the result a learner should be able to predict after reading the page.
In the spring > spring-data-jpa page, the notes should connect the definition with a working scenario, a mistake that beginners actually make, and the exact check that proves the fix. That makes the topic useful for coding, debugging, and interview revision.
Spring Data JPA is part of the Spring Data family. It simplifies data access by providing repository abstractions on top of JPA (Java Persistence API). Instead of writing boilerplate CRUD code, you define an interface and Spring generates the implementation automatically.
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_email", columnList = "email", unique = true)
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // AUTO_INCREMENT
private Long id;
@Column(name = "username", nullable = false, length = 50, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@Enumerated(EnumType.STRING) // Store enum as string in DB
@Column(nullable = false)
private Role role = Role.USER;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Transient // Not persisted to DB
private String fullDisplayName;
// One user has many orders
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders;
@PrePersist
protected void onCreate() { createdAt = LocalDateTime.now(); }
@PreUpdate
protected void onUpdate() { updatedAt = LocalDateTime.now(); }
// Constructors, getters, setters...
public enum Role { USER, ADMIN, MODERATOR }
}
package com.example.repository;
import com.example.entity.User;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.domain.*;
import org.springframework.data.repository.query.Param;
import java.util.*;
// JpaRepository provides: save, findById, findAll, delete, count, exists, etc.
public interface UserRepository extends JpaRepository<User, Long> {
// Spring generates query from method name
Optional<User> findByEmail(String email);
Optional<User> findByUsername(String username);
List<User> findByRole(User.Role role);
boolean existsByEmail(String email);
long countByRole(User.Role role);
// Derived query with multiple conditions
List<User> findByRoleAndEmailContaining(User.Role role, String emailPart);
// Custom JPQL query
@Query("SELECT u FROM User u WHERE u.email = :email AND u.role = :role")
Optional<User> findByEmailAndRole(@Param("email") String email,
@Param("role") User.Role role);
// Native SQL query
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findUsersCreatedAfter(@Param("date") java.time.LocalDateTime date);
// Pagination and sorting
Page<User> findByRole(User.Role role, Pageable pageable);
// Modifying query (UPDATE/DELETE)
@Modifying
@Transactional
@Query("UPDATE User u SET u.password = :password WHERE u.id = :id")
int updatePassword(@Param("id") Long id, @Param("password") String password);
// Delete by field
void deleteByEmail(String email);
}
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
if (userRepository.existsByEmail(user.getEmail())) {
throw new RuntimeException("Email already exists: " + user.getEmail());
}
return userRepository.save(user);
}
@Transactional(readOnly = true) // Optimization for read-only operations
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found: " + id));
}
@Transactional(readOnly = true)
public Page<User> getUsers(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).ascending());
return userRepository.findAll(pageable);
}
public User updateUser(Long id, User updatedUser) {
User existing = getUserById(id);
existing.setUsername(updatedUser.getUsername());
existing.setEmail(updatedUser.getEmail());
return userRepository.save(existing); // save() = INSERT or UPDATE
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
When studying Spring Data JPA Repositories, Queries, Pagination, separate three things: the concept, the syntax, and the situation where it is useful. This prevents the lesson from becoming a list of commands with no practical meaning.
In Spring, Spring Data JPA Repositories, Queries, Pagination becomes easier when you build a tiny example first, then increase complexity. Add one realistic input, one invalid or boundary input, and one explanation of why the result changes.
class SpringDataJPARepositoriesQueriesPaginationReview {
public static void main(String[] args) {
String state = "ready";
System.out.println("Spring Data JPA Repositories Queries Pagination: " + state);
}
}
String value = null;
if (value == null) {
System.out.println("Spring Data JPA Repositories Queries Pagination: handle the missing value before continuing");
}
Memorizing Spring Data JPA Repositories Queries Pagination without the situation where it is useful.
Connect Spring Data JPA Repositories Queries Pagination to a concrete Spring task.
Testing Spring Data JPA Repositories Queries Pagination only with the perfect input.
Include empty, missing, duplicate, incompatible, or failed cases when relevant.
Changing code before reading the visible symptom or error message.
Inspect the output, state, configuration, or stack trace connected to Spring Data JPA Repositories Queries Pagination.
Memorizing Spring Data JPA Repositories Queries Pagination without the situation where it is useful.
Connect Spring Data JPA Repositories Queries Pagination to a concrete Spring task.
The common mistake is memorizing syntax without understanding when the behavior changes or fails.
Remember the problem it solves in Spring, then attach the syntax or steps to that problem.
You can predict the result of a small example, explain a failure case, and choose it over a nearby alternative for a clear reason.
They often copy the syntax but skip the state, input, dependency, selector, route, type, or configuration that controls the behavior.
Explore 500+ free tutorials across 20+ languages and frameworks.