How do derived query methods work (e.g., findByLastNameAndAge)?
Quick Answer
Spring Data JPA parses a repository method's name at startup, splitting it into keywords (findBy, And, OrderBy, ...) and property names matching the entity's fields, then automatically generates the corresponding JPQL query — so a method named findByLastNameAndAge(String lastName, int age) is translated into a query filtering by both properties, with no query string or annotation required at all.
Detailed Answer
Spring Data JPA can derive a working query purely from a repository method's name, by parsing it against a defined grammar at application startup:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastName(String lastName);
List<User> findByLastNameAndAge(String lastName, int age);
List<User> findByAgeGreaterThan(int age);
List<User> findByLastNameOrderByAgeDesc(String lastName);
boolean existsByEmail(String email);
long countByStatus(String status);
}
How the parsing works: Spring Data splits the method name after the introductory keyword (findBy, existsBy, countBy, deleteBy, ...) into segments separated by And/Or, matching each segment against the entity's property names (case-insensitively, and it also understands nested properties like findByAddress_City). Comparison keywords (GreaterThan, Between, Like, IsNull, Containing, StartingWith, ...) modify how a given property is compared. OrderBy<Property><Direction> appends a sort clause.
At application startup, Spring Data validates each declared repository method's name against the entity's actual properties — an invalid property name or malformed keyword combination fails immediately with a clear error, rather than silently doing the wrong thing at runtime.
Internally, each derived method compiles down to an equivalent JPQL query (or, for MongoDB/other Spring Data modules, an equivalent native query for that store) executed the same way a hand-written @Query-annotated method would be.
Trade-off: derived query names are convenient and self-documenting for simple filters, but get unwieldy and hard to read once a query needs more than 2-3 conditions (findByLastNameAndAgeGreaterThanAndStatusInOrderByCreatedAtDesc is technically valid but painful) — at that point, an explicit @Query annotation (or the Criteria API/Querydsl for fully dynamic queries) is usually clearer.