Generally, the query creation mechanism for JPA works as described in Query Methods. The following example shows what a JPA query method translates into:

public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

We create a query using the JPA criteria API from this, but, essentially, this translates into the following query: select u from User u where u.emailAddress = ?1 and u.lastname = ?2. Spring Data JPA does a property check and traverses nested properties, as described in Property Expressions.

The following table describes the keywords supported for JPA and what a method containing that keyword translates to:

Keyword Sample JPQL snippet
Distinct findDistinctByLastnameAndFirstname select distinct …​ where x.lastname = ?1 and x.firstname = ?2
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
IsEquals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNullNull findByAge(Is)Null … where x.age is null
IsNotNullNotNull findByAge(Is)NotNull … where x.age is not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstname) = UPPER(?1)

<aside> ℹ️ Note : In and NotIn also take any subclass of Collection as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check Repository query keywords.

</aside>

<aside> ⚠️ Warning : DISTINCT can be tricky and not always producing the results you expect. For example, select distinct u from User u will produce a complete different result than select distinct u.lastname from User u. In the first case, since you are including User.id, nothing will duplicated, hence you’ll get the whole table, and it would be of User objects. However, that latter query would narrow the focus to just User.lastname and find all unique last names for that table. This would also yield a List<String> result set instead of a List<User> result set. countDistinctByLastname(String lastname) can also produce unexpected results. Spring Data JPA will derive select count(distinct u.id) from User u where u.lastname = ?1. Again, since u.id won’t hit any duplicates, this query will count up all the users that had the binding last name. Which would the same as countByLastname(String lastname)! What is the point of this query anyway? To find the number of people with a given last name? To find the number of distinct people with that binding last name? To find the number of distinct last names? (That last one is an entirely different query!) Using distinct sometimes requires writing the query by hand and using @Query to best capture the information you seek, since you also may be needing a projection to capture the result set.

</aside>