← Back to list

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.

Ayoub Taouam · 2025-11-27 12:11 · 2 claps · 3.5 min read
#java #spring-boot #spring-jpa #jpql #sql
Open on Medium ↗

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:

  1. Provides repository abstractions: You write an interface, and Spring generates the implementation.
  2. 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 B
  • OrderByAgeDesc: ORDER BY age DESC
  • IsNull: 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