An Introduction to Spring Data JPA: Building Efficient Queries with Spring Boot
Learn how to simplify database queries in Spring Boot using Spring Data JPA for clean, efficient, and maintainable code.
An Introduction to Spring Data JPA: Building Efficient Queries with Spring Boot

Working with relational databases is a core part of almost any backend application. But handling SQL manually everywhere gets messy.
That’s where Spring Data JPA steps in.
Spring Data JPA layers powerful abstraction on top of JPA (Java Persistence API), letting you:
- Map Java classes to database tables
- Perform CRUD operations without writing SQL
- Build queries using method names, JPQL or native SQL
- Use pagination and sorting with minimal boilerplate
This article gives you a practical and crystal-clear introduction to Spring Data JPA, with examples that work exactly as you’d build them in a real Spring Boot project.
What Is Spring Data JPA?
Spring Data JPA is a module that simplifies database operations.
At its core, it does two big things:
- Provides repository abstractions: You write an interface, and Spring generates the implementation.
- Simplifies query creation: You can query data using:
- Method names (
findByEmail) - JPQL (Java Persistence Query Language)
- Native SQL
With it, you quickly gain clean, maintainable data-access code.
Project Setup:
Your pom.xml needs:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
And database configuration:
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=pass
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Creating an Entity
An entry is simply a Java class mapped to a database table.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private Integer age;
// getters + setters
}
what this does:
@Entity: marks as JPA entity@Table: sets table name@Id: primary key@GeneratedValue: auto-increment
Creating a Repository (No Implementation Needed)
Spring Data JPA implements everything behind the scenes:
public interface UserRepository extends JpaRepository<User, Long> {
}
By extending JpaRepository, you get:
save()findAll()findById()deleteById()count()- pagination methods
- sorting methods
…all for free!!!
Building Queries with Method Names
Spring Data JPA can generate SQL based on method names:
List<User> findByName(String name);
List<User> findByAgeGreaterThan(int age);
List<User> findByEmailContaining(String domain);
Spring reads the method name and builds the query automatically.
Some keywords you can use:
Containing:Like %value%GreaterThan:>Between:BETWEEN A AND BOrderByAgeDesc:ORDER BY age DESCIsNull:IS NULL
Example:
List<User> findByAgeBetween(int min, int max);
This genertes:
SELECT * FROM users WHERE age BETWEEN ? AND ?
Building Queries Using JPQL
When your query becomes too complex for method names, you can write JPQL.
JPQL = SQL-like syntax, but using entity names, not table names.
@Query("SELECT u FROM User u WHERE u.age > :age")
List<User> getUsersOlderThan(@Param("age") int age);
JPQL uses Java fields, not SQL column names.
Another example:
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain% ORDER BY u.age DESC")
List<User> findByEmailDomainSorted(@Param("domain") String domain);
Building Native SQL Queries
Sometimes JPQL isn’t enough, you want actual SQL.
@Query(
value = "SELECT * FROM users WHERE age > ?1",
nativeQuery = true
)
List<User> getUsersNative(int age);
Use native SQL carefully:
- It ties you to a specific database vendor
- You lode portability
- You must reference table + column names directly
But for performance-critical cases, native queries are very useful.
Pagination and Sorting: Must-Know Feature
Pagination and sorting are extremely easy:
Fetch a page of users sorted by age
Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending());
Page<User> page = userRepository.findAll(pageable);
Breaking it down:
PageRequest.of(pageNumber, pageSize, sort)- Page numbers start at 0
Page<User>contains metadata like: total pages, total elements,hasNext(),hasPrevious()
Example Controller
@GetMapping("/users")
public Page<User> getUsers(
@RequestParam int page,
@RequestParam int size,
@RequestParam(defaultValue = "id") String sortBy
) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
return userRepository.findAll(pageable);
}
Call:
GET /users?page=0&size=5&sortBy=age
Combining Pagination with Custom Queries
JPQL query with pagination:
@Query("SELECT u FROM User u WHERE u.age > :age")
Page<User> findByAgeGreaterThan(@Param("age") int age, Pageable pageable);
Native query with pagination:
@Query(
value = "SELECT * FROM users WHERE age > ?1",
countQuery = "SELECT count(*) FROM users WHERE age > ?1",
nativeQuery = true
)
Page<User> findOlderThan(int age, Pageable pageable);
Example: Good Practice
Service Layer
Service layer keeps your logic clean:
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) {
this.repo = repo;
}
public Page<User> getUsers(int page, int size, String sort) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sort));
return repo.findAll(pageable);
}
public List<User> searchByEmailDomain(String domain) {
return repo.findByEmailContaining(domain);
}
}
Controller Layer
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService service;
public UserController(UserService service) {
this.service = service;
}
@GetMapping
public Page<User> list(
@RequestParam int page,
@RequestParam int size,
@RequestParam(defaultValue = "id") String sort
) {
return service.getUsers(page, size, sort);
}
}
Best Practices With Spring Data JPA
- Keep entities lightweight: Avoid putting business logic directly inside entities.
- Prefer JPQL for complex queries: It keeps you code portable across relational databases.
- Use pagination for large datasets: Never return thousands of rows at once.
- Avoid native SQL unless necessary: Native queries bypass JPA features.
- Keep repository methods focused: Don’t build huge, confusing query methods.
Wrap Up
Spring Data JPA makes database development in Spring Boot clean, efficient and maintainable.
With just an interface and a couple of annotations, you get:
- Auto-generated CRUD operations
- JPQL & native SQL flexibility
- Pagination & sorting support
- Cleaner service and controller layers
It’s one of the reasons Spring Boot is so productive, you write less code while keeping full power over your queries.
Follow for more!!!
메타데이터
- post_id
- 09a43aab00f2
- slug
- an-introduction-to-spring-data-jpa-building-efficient-queries-with-spring-boot-09a43aab00f2
- url
- https://medium.com/@ayoubtaouam/an-introduction-to-spring-data-jpa-building-efficient-queries-with-spring-boot-09a43aab00f2
- canonical_url
- https://medium.com/@ayoubtaouam/an-introduction-to-spring-data-jpa-building-efficient-queries-with-spring-boot-09a43aab00f2
- author_url
- https://medium.com/@ayoubtaouam
- status
- ok
- fetched_at
- 2026-06-26 03:39:16