RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
MediumSpring Boot#386 min readJul 11, 2026

Sorting in Spring Data.

spring-boot
spring-data
rest-api
jpa
Practice modeTest yourself instead of reading straight through

Hook: Sorting is one of those tiny API features that makes a service feel polished instead of random.

Question: What does sorting mean in Spring Data, and how do you use it in a Spring Boot REST API?

Answer: In Spring Data, sorting means asking a repository to return rows in a specific order, such as newest first or cheapest first. In REST APIs, Spring MVC can turn query parameters like ?sort=price,desc into a Sort object automatically, and you pass that into repository methods like findAll(sort) or findAll(pageable). The key detail is that Spring Data sorts by entity property names, not database column names.

Interview-Ready Answer: I use Spring Data sorting by passing a Sort or Pageable into repository methods. In a REST API, Spring Boot can bind query params like ?sort=price,desc&sort=name,asc directly, so the client controls the order without me writing SQL. If there is no sort, I usually set a safe default, and I always remember that the sort field must match the JPA entity property name, not the table column.

🧠 Memory Map
Memory map — visual summary of this topic

What sorting is

Detailed Explanation: A Sort object is a small, immutable value object that describes order rules, such as ascending by price and then ascending by name. Spring Data does not sort by magic inside your controller; it passes that intent down to the repository layer so the database can add an ORDER BY clause.

How it works under the hood

  1. The client sends a request such as /products?sort=price,desc&sort=name,asc.
  2. Spring MVC binds those query parameters into a Sort or Pageable argument. Sort only describes ordering, while Pageable describes ordering plus page number and page size.
  3. Your controller calls a repository method such as findAll(sort) or findAll(pageable).
  4. Spring Data translates the sort rule into the query it builds for JPA or another store. In JPA, that becomes SQL ORDER BY on the entity property path.
  5. The database executes the ordering, then returns the rows. If pagination is also present, the database sorts first and then returns only the requested page.
  6. The response goes back to the client in the chosen order, which is why the same endpoint can serve price,desc, name,asc, or multiple fields without changing code.

When and why to use it

  • Use Sort when the client only needs ordering.
  • Use Pageable when the client needs both ordering and paging, which is common for search screens and admin tables.
  • Use a fixed @Query ORDER BY when the order is business-critical and should not be client-controlled.

Sort vs Pageable vs @Query

ApproachBest forProsWatch out
SortSimple orderingFlexible, clean APINo paging
PageableTables, listsSort + page + sizeDefault size is often 20
@Query ORDER BYFixed orderVery explicitLess flexible

Performance and edge cases

  • Complexity: if the sort happens in Java, sorting is typically O(n log n); in Spring Data JPA, the database does the work, so performance depends on indexes and row count.
  • Indexed columns are fast: sorting 100,000 rows by an indexed field is much safer than sorting 100,000 rows by an unindexed text field.
  • Property names matter: sort=created_at usually fails if the entity field is createdAt. Spring Data uses Java property names, not table column names.
  • Multiple fields are tie-breakers: price,desc then name,asc means price decides first, and name breaks ties.
  • Unsorted is not stable: if you do not specify a sort, the database is free to return rows in any order, which can change between requests.
  • Page defaults: Spring Data Web typically uses page 0 and size 20 unless you override it.

Memory note: think of Sort as the order on a shelf and Pageable as which slice of that shelf you take.

Real-World Example: In an e-commerce catalog service, the frontend product grid often sends requests like /products?sort=price,asc or /products?page=0&size=24&sort=createdAt,desc. The backend uses Spring Data sorting so shoppers can switch between cheapest, newest, and best-selling views without new endpoints. If someone mistakenly sends a database column name such as sort=created_at instead of the entity field createdAt, the service can throw a data-access error and the product page returns a 400 or 500. In production, that shows up as blank grids, stack traces mentioning PropertyReferenceException, and angry support tickets saying, "the filter works but the sort is broken."

Spring Boot
# File: src/main/resources/application.properties
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# File: src/main/java/com/example/sorting/SortingApplication.java
package com.example.sorting;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@SpringBootApplication
public class SortingApplication {

    public static void main(String[] args) {
        SpringApplication.run(SortingApplication.class, args);
    }

    @Bean
    CommandLineRunner seed(ProductRepository repository) {
        return args -> repository.saveAll(List.of(
                new Product("Keyboard", "Electronics", new BigDecimal("79.99")),
                new Product("Mouse", "Electronics", new BigDecimal("29.99")),
                new Product("Desk Chair", "Furniture", new BigDecimal("199.00")),
                new Product("Notebook", "Stationery", new BigDecimal("4.50"))
        ));
    }
}

@Entity
@Table(name = "products")
class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String category;

    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal price;

    protected Product() {
        // JPA needs a no-arg constructor.
    }

    Product(String name, String category, BigDecimal price) {
        this.name = name;
        this.category = category;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getCategory() {
        return category;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

interface ProductRepository extends JpaRepository<Product, Long> {
    // Spring Data automatically gives us findAll(Sort) and findAll(Pageable).
}

@RestController
@RequestMapping("/products")
class ProductController {

    private final ProductRepository repository;

    ProductController(ProductRepository repository) {
        this.repository = repository;
    }

    @GetMapping
    List<Product> all(Sort sort) {
        // Spring MVC binds ?sort=price,desc&sort=name,asc into this Sort object.
        // If the client does not send a sort, keep the output stable with a safe default.
        Sort effectiveSort = (sort == null || sort.isUnsorted())
                ? Sort.by("id").ascending()
                : sort;

        return repository.findAll(effectiveSort);
    }

    @GetMapping("/page")
    Page<Product> page(Pageable pageable) {
        // Pageable carries page, size, and sort together.
        // Default page size is 20 unless the client overrides it.
        return repository.findAll(pageable);
    }
}

@RestControllerAdvice
class ApiErrors {

    @ExceptionHandler({PropertyReferenceException.class, InvalidDataAccessApiUsageException.class})
    ResponseEntity<Map<String, String>> badSort(Exception ex) {
        // A typo like sort=created_at,desc becomes a clean 400 instead of a stack trace.
        return ResponseEntity.badRequest().body(Map.of(
                "error", "Invalid sort field. Use the entity property name, not the column name.",
                "details", ex.getMessage()
        ));
    }
}

Follow-up & Tricky Questions:

  • How is Sort different from Pageable? Sort only controls order, while Pageable controls page number, page size, and order together. In Spring Data REST APIs, Pageable is usually the better choice when the client needs a grid or list view.
  • How do you sort by multiple fields? You chain them, for example price first and name second. Spring Data applies them left to right so the first field is the main order and the next fields break ties.
  • Can you sort by a nested property? Yes, if the mapping supports it, such as customer.address.city. Be careful: nested sorts often imply joins, which can be slower on large tables.
  • What happens if the sort field is wrong? Spring Data usually fails at query time with a data-access exception such as PropertyReferenceException. In an API, that should become a clear 400 response, not a cryptic stack trace.
  • How do indexes affect sorting? A database index on the sorted column can make the query much faster because the database can read rows in order. Without an index, the database may sort a large result set in memory or use temporary disk space.
  • Does Spring sort by column name or property name? Property name. That is one of the most common interview traps.
  • Is the result order guaranteed if I do not sort? No. Without an explicit order, SQL does not promise a stable result sequence.
  • Tricky: if I ask for ?sort=price, what direction is used? Ascending by default. The direction only changes to descending when you say ,desc.
  • Tricky: if I use Pageable, does it sort before or after paging? Sort happens first, then paging. That is why page 0 and page 1 are consistent slices of the same order.

Common Mistakes:

  • Sorting by column names: Candidates write created_at because that is the database column. Correction: sort by the entity field, like createdAt.
  • Forgetting a default order: They trust the database to return rows in a nice sequence. Correction: set a default sort when the client does not send one.
  • Using sort for huge unindexed tables: They think sorting is always cheap. Correction: add indexes for common sort fields, especially on large read-heavy tables.
  • Ignoring pagination: They return the whole table and then sort it in memory. Correction: use Pageable for API lists that can grow.

Memory Hook: Think of Sort like the order of books on a shelf, and Pageable like which shelf slice you take home. The shelf order is the sort; the slice is the page.

Cheat Sheet:

  • Sort = order only.
  • Pageable = page + size + sort.
  • Spring Data uses entity property names, not SQL column names.
  • Default direction is ascending.
  • No sort means no guaranteed order.
  • Indexes matter a lot for large sorted queries.

Practice Tasks:

  • Build /products?sort=price,desc and verify the order changes.
  • Add a second field to the sort, such as name ascending as a tie-breaker.
  • Break it on purpose with a wrong field name, then make the API return a clean 400.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

# File: src/main/resources/application.properties spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true # File: src/main/java/com/example/sorting/SortingApplication.java package com.example.sorting; import java.math.BigDecimal; import java.util.List; import java.util.Map; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestControllerAdvice; @SpringBootApplication public class SortingApplication { public static void main(String[] args) { SpringApplication.run(SortingApplication.class, args); } @Bean CommandLineRunner seed(ProductRepository repository) { return args -> repository.saveAll(List.of( new Product("Keyboard", "Electronics", new BigDecimal("79.99")), new Product("Mouse", "Electronics", new BigDecimal("29.99")), new Product("Desk Chair", "Furniture", new BigDecimal("199.00")), new Product("Notebook", "Stationery", new BigDecimal("4.50")) )); } } @Entity @Table(name = "products") class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false) private String category; @Column(nullable = false, precision = 10, scale = 2) private BigDecimal price; protected Product() { // JPA needs a no-arg constructor. } Product(String name, String category, BigDecimal price) { this.name = name; this.category = category; this.price = price; } public Long getId() { return id; } public String getName() { return name; } public String getCategory() { return category; } public BigDecimal getPrice() { return price; } public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setCategory(String category) { this.category = category; } public void setPrice(BigDecimal price) { this.price = price; } } interface ProductRepository extends JpaRepository<Product, Long> { // Spring Data automatically gives us findAll(Sort) and findAll(Pageable). } @RestController @RequestMapping("/products") class ProductController { private final ProductRepository repository; ProductController(ProductRepository repository) { this.repository = repository; } @GetMapping List<Product> all(Sort sort) { // Spring MVC binds ?sort=price,desc&sort=name,asc into this Sort object. // If the client does not send a sort, keep the output stable with a safe default. Sort effectiveSort = (sort == null || sort.isUnsorted()) ? Sort.by("id").ascending() : sort; return repository.findAll(effectiveSort); } @GetMapping("/page") Page<Product> page(Pageable pageable) { // Pageable carries page, size, and sort together. // Default page size is 20 unless the client overrides it. return repository.findAll(pageable); } } @RestControllerAdvice class ApiErrors { @ExceptionHandler({PropertyReferenceException.class, InvalidDataAccessApiUsageException.class}) ResponseEntity<Map<String, String>> badSort(Exception ex) { // A typo like sort=created_at,desc becomes a clean 400 instead of a stack trace. return ResponseEntity.badRequest().body(Map.of( "error", "Invalid sort field. Use the entity property name, not the column name.", "details", ex.getMessage() )); } }