RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

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

Derived Query Methods.

practicelearning
Practice modeTest yourself instead of reading straight through

Hook: Interviewers love this because it shows whether you can let Spring Data write the query for you without losing control of the database behavior.

Question: What are derived query methods in Spring Data JPA?

Answer: Derived query methods are repository methods whose names Spring Data JPA reads and turns into database queries. For example, findByLastName, countByActiveTrue, or findTop3ByOrderByCreatedAtDesc all work without writing SQL or JPQL. They are great for simple lookups because they are quick to write and easy to read, and invalid property names usually fail fast when the repository starts.

Interview-Ready Answer: In Spring Data JPA, derived query methods are repository methods where the method name itself describes the query. I can write something like findByEmail or findByLastNameAndActiveTrue, and Spring parses that name, validates the entity fields, and generates the query automatically. I use them for simple, readable queries, but if the logic gets complex or I need more control, I switch to @Query or Specifications.

🧠 Memory Map
Memory map — visual summary of this topic

What it is

Derived queries are also called query derivation: you express the intent in the repository method name, and Spring Data builds the query for you. The name is made of a verb-like prefix such as find, count, exists, or delete, followed by By, then the filter conditions.

How it works under the hood

  1. Spring creates a proxy (a stand-in object) for your repository interface so your code can call methods as if they were normal Java methods.
  2. For each method, Spring Data uses a query lookup strategy to decide whether it should use an annotated query, a named query, or a derived query.
  3. For derived queries, Spring parses the method name with an internal parser called PartTree. That parser breaks the name into parts like And, Or, Between, IgnoreCase, OrderBy, Top, and First.
  4. Spring checks the property path against your entity model. If you wrote findByEmaiil but the entity field is email, the application usually fails fast with a PropertyReferenceException.
  5. Spring generates the JPQL or SQL equivalent and caches the query metadata so later calls do not re-parse the method name every time.
  6. When you call the method, Spring binds the method arguments, executes the query, and maps the result into the declared return type such as List, Optional, boolean, or Page.

When to use it

Use derived queries when the query is short, stable, and maps directly to entity fields. Good examples are lookup-by-email, filter-by-status, existence checks, count queries, and simple ordered lists. If the method starts reading like a paragraph, that is your cue to switch to @Query or Specifications.

Derived query vs other options

OptionBest forTrade-off
Derived querySimple lookupsVery readable until it gets long
@QueryComplex joinsYou write the query yourself
SpecificationsDynamic filtersMore code, more flexible

Performance and edge cases

The parsing cost is tiny and happens at startup or repository creation time; think O(n) in method-name length, usually microseconds per method. After that, the important cost is the database round trip, which is often 1-10 ms on a healthy system, so derived queries are usually not slower than handwritten JPQL in any meaningful way.

  • Single-result methods: If you return Optional<Customer> or a single entity and the database returns two rows, Spring throws IncorrectResultSizeDataAccessException.
  • Ordering: findTopBy... or findFirstBy... without OrderBy is not deterministic; the database can pick any matching row.
  • Case handling: IgnoreCase is for string comparisons. It does not magically make every type case-insensitive.
  • Long names: The runtime is fine, but unreadable names become a maintenance problem fast.

Real-World Example: Imagine a checkout service in an e-commerce app. The team uses findByEmail to load the customer account and findTop3ByActiveTrueOrderByCreatedAtDesc to show recent active customers in an admin dashboard. Everything is simple and fast to read, which is exactly why derived queries are popular in production.

Now the bug: an old data migration accidentally created two rows with the same email address. The repository method still compiled and the app still started, but the first request that called findByEmail crashed with IncorrectResultSizeDataAccessException. Users saw 500 errors during login or checkout, and the logs showed a message like query did not return a unique result.

That is the key lesson: derived queries do not replace data rules. If a method is supposed to return one row, the database should also enforce uniqueness with a unique constraint, otherwise Spring can only detect the problem at runtime.

Spring Boot
package com.example.demo;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
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.IncorrectResultSizeDataAccessException;
import org.springframework.data.jpa.repository.JpaRepository;

@SpringBootApplication
public class DerivedQueryMethodsApplication {

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

    @Bean
    CommandLineRunner demo(CustomerRepository repo) {
        return args -> {
            repo.saveAllAndFlush(List.of(
                new Customer("Ava", "Patel", "ava@example.com", true,
                        LocalDateTime.now().minusDays(2), new Address("Boston", "1 Main St")),
                new Customer("Noah", "Patel", "noah@example.com", false,
                        LocalDateTime.now().minusDays(1), new Address("Boston", "2 Main St")),
                new Customer("Mia", "Chen", "mia@example.com", true,
                        LocalDateTime.now().minusHours(5), new Address("Austin", "3 Main St")),
                new Customer("Ivy", "Chen", "dup@example.com", true,
                        LocalDateTime.now().minusHours(2), new Address("Austin", "4 Main St")),
                new Customer("Ethan", "Stone", "dup@example.com", false,
                        LocalDateTime.now().minusHours(1), new Address("Dallas", "5 Main St"))
            ));

            System.out.println("=== derived queries ===");
            System.out.println("Patel customers: " + repo.findByLastNameIgnoreCase("patel"));
            System.out.println("Austin customers: " + repo.findByAddressCity("Austin"));
            System.out.println("Active Patels: " + repo.findByLastNameAndActiveTrueOrderByCreatedAtDesc("Patel"));
            System.out.println("Top active customers: " + repo.findTop3ByActiveTrueOrderByCreatedAtDesc());
            System.out.println("Active count: " + repo.countByActiveTrue());
            System.out.println("Ava exists? " + repo.existsByEmail("ava@example.com"));
            System.out.println("Missing email Optional: " + repo.findByEmail("missing@example.com"));

            try {
                // This fails because the method expects one row, but the database has two matches.
                System.out.println("Duplicate email lookup: " + repo.findByEmail("dup@example.com"));
            } catch (IncorrectResultSizeDataAccessException ex) {
                String message = ex.getMostSpecificCause() != null ? ex.getMostSpecificCause().getMessage() : ex.getMessage();
                System.out.println("Expected failure: " + ex.getClass().getSimpleName());
                System.out.println("Message: " + message);
            }
        };
    }
}

@Entity
@Table(name = "customers")
class Customer {

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

    private String firstName;
    private String lastName;
    private String email;
    private boolean active;
    private LocalDateTime createdAt;

    @Embedded
    private Address address;

    protected Customer() {
    }

    Customer(String firstName, String lastName, String email, boolean active, LocalDateTime createdAt, Address address) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.active = active;
        this.createdAt = createdAt;
        this.address = address;
    }

    @Override
    public String toString() {
        return firstName + " " + lastName + " <" + email + "> " + address + " active=" + active;
    }
}

@Embeddable
class Address {

    private String city;
    private String street;

    protected Address() {
    }

    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }

    @Override
    public String toString() {
        return city + ", " + street;
    }
}

interface CustomerRepository extends JpaRepository<Customer, Long> {

    List<Customer> findByLastNameIgnoreCase(String lastName);

    List<Customer> findByAddressCity(String city);

    List<Customer> findByLastNameAndActiveTrueOrderByCreatedAtDesc(String lastName);

    List<Customer> findTop3ByActiveTrueOrderByCreatedAtDesc();

    long countByActiveTrue();

    boolean existsByEmail(String email);

    Optional<Customer> findByEmail(String email);
}

Follow-up & Tricky Questions:

  • How does Spring decide between a derived query, a named query, and @Query? By default, Spring Data first looks for an explicitly declared query, then a named query, and then it falls back to query derivation. That means an annotated query wins when you need exact control.
  • Can derived queries navigate relationships? Yes, simple path traversal works, such as findByAddressCity for an embedded object or association. Once the path becomes a real join-heavy report, readability usually drops and @Query becomes cleaner.
  • What return types can I use? Common ones are List, Optional, a single entity, Page, Slice, long, and boolean. The return type should match the meaning of the query, not just whatever compiles.
  • How does sorting work in a derived query? You can hard-code it with OrderBy inside the method name, or pass a Sort / Pageable parameter. If you need user-driven sorting, prefer parameters instead of baking every order into the method name.
  • Do derived queries save time at runtime? Not much compared with handwritten JPQL. The main win is developer speed and readability; the actual database work is what dominates latency.
  • Can I use derived queries for deletes? Yes, methods like deleteByStatus are supported, but they are destructive, so you should be careful with transactions and business rules.
  • Does findTopBy... mean the newest row? Not by itself. Without an explicit OrderBy, the database can return any matching row, so the result is not stable.
  • Is existsBy just countBy(...) > 0? Conceptually, yes, but existsBy is clearer and may let the provider produce a more efficient query.
  • What happens if a field name is misspelled? Spring usually fails early with a property resolution error when the repository is created, which is good because you find the bug before the app serves traffic.

Common Mistakes:

  • Making method names too long. If the name starts reading like a sentence, move to @Query or Specifications.
  • Assuming one row when the data is not unique. If the column is not guaranteed unique, return List or add a database unique constraint.
  • Forgetting that order is not automatic. Add OrderBy or use Sort/Pageable when the row order matters.
  • Expecting every kind of condition to fit in the method name. Complex joins, subqueries, and report-style filters are usually better as explicit queries.

Memory Hook: Think of the method name like a recipe card: Spring reads the words, turns them into a query, and serves the result. If the recipe card becomes a novel, it is time to stop using it.

Cheat Sheet:

  • findBy... returns rows that match a property filter.
  • countBy... returns a count.
  • existsBy... returns a yes/no answer.
  • Top and First limit the result size, usually to 1 if no number is written.
  • And, Or, IgnoreCase, and OrderBy are common building blocks.
  • Use derived queries for simple cases; use @Query or Specifications when logic gets complex.

Practice Tasks:

  • Add findByFirstNameStartingWith and print the result for names starting with A.
  • Replace one list query with a Page<Customer> method and try paging the results.
  • Break the method name on purpose, restart the app, and read the startup error so you recognize the failure pattern in real projects.
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

package com.example.demo; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import jakarta.persistence.Embeddable; import jakarta.persistence.Embedded; 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.IncorrectResultSizeDataAccessException; import org.springframework.data.jpa.repository.JpaRepository; @SpringBootApplication public class DerivedQueryMethodsApplication { public static void main(String[] args) { SpringApplication.run(DerivedQueryMethodsApplication.class, args); } @Bean CommandLineRunner demo(CustomerRepository repo) { return args -> { repo.saveAllAndFlush(List.of( new Customer("Ava", "Patel", "ava@example.com", true, LocalDateTime.now().minusDays(2), new Address("Boston", "1 Main St")), new Customer("Noah", "Patel", "noah@example.com", false, LocalDateTime.now().minusDays(1), new Address("Boston", "2 Main St")), new Customer("Mia", "Chen", "mia@example.com", true, LocalDateTime.now().minusHours(5), new Address("Austin", "3 Main St")), new Customer("Ivy", "Chen", "dup@example.com", true, LocalDateTime.now().minusHours(2), new Address("Austin", "4 Main St")), new Customer("Ethan", "Stone", "dup@example.com", false, LocalDateTime.now().minusHours(1), new Address("Dallas", "5 Main St")) )); System.out.println("=== derived queries ==="); System.out.println("Patel customers: " + repo.findByLastNameIgnoreCase("patel")); System.out.println("Austin customers: " + repo.findByAddressCity("Austin")); System.out.println("Active Patels: " + repo.findByLastNameAndActiveTrueOrderByCreatedAtDesc("Patel")); System.out.println("Top active customers: " + repo.findTop3ByActiveTrueOrderByCreatedAtDesc()); System.out.println("Active count: " + repo.countByActiveTrue()); System.out.println("Ava exists? " + repo.existsByEmail("ava@example.com")); System.out.println("Missing email Optional: " + repo.findByEmail("missing@example.com")); try { // This fails because the method expects one row, but the database has two matches. System.out.println("Duplicate email lookup: " + repo.findByEmail("dup@example.com")); } catch (IncorrectResultSizeDataAccessException ex) { String message = ex.getMostSpecificCause() != null ? ex.getMostSpecificCause().getMessage() : ex.getMessage(); System.out.println("Expected failure: " + ex.getClass().getSimpleName()); System.out.println("Message: " + message); } }; } } @Entity @Table(name = "customers") class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String firstName; private String lastName; private String email; private boolean active; private LocalDateTime createdAt; @Embedded private Address address; protected Customer() { } Customer(String firstName, String lastName, String email, boolean active, LocalDateTime createdAt, Address address) { this.firstName = firstName; this.lastName = lastName; this.email = email; this.active = active; this.createdAt = createdAt; this.address = address; } @Override public String toString() { return firstName + " " + lastName + " <" + email + "> " + address + " active=" + active; } } @Embeddable class Address { private String city; private String street; protected Address() { } Address(String city, String street) { this.city = city; this.street = street; } @Override public String toString() { return city + ", " + street; } } interface CustomerRepository extends JpaRepository<Customer, Long> { List<Customer> findByLastNameIgnoreCase(String lastName); List<Customer> findByAddressCity(String city); List<Customer> findByLastNameAndActiveTrueOrderByCreatedAtDesc(String lastName); List<Customer> findTop3ByActiveTrueOrderByCreatedAtDesc(); long countByActiveTrue(); boolean existsByEmail(String email); Optional<Customer> findByEmail(String email); }