1. Introduction
Spring Dat;u JPA provides a query derivation feature, using which we can derive queries automatically by just following the method name conventions.
In this article, we’ll use the query derivation feature to find entities by one or more columns.
2. Example Setup
For example purposes, we’ll use an Account entity which contains properties related to the user account:
@Entity
@Table(name = "ACCOUNTS")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accounts_seq")
@SequenceGenerator(name = "accounts_seq", sequenceName = "accounts_seq", allocationSize = 1)
@Column(name = "user_id")
private int userId;
private String username;
private String password;
private String email;
private Timestamp createdOn;
private Timestamp lastLogin;
@OneToOne
@JoinColumn(name = "permissions_id")
private Permission permission;
// getters and setters
}
For demo purposes, we’ll also add some sample data to the Accounts table:
userId | username | password | createdOn | lastLogin | permission | |
---|---|---|---|---|---|---|
1 | user_admin | 737d4251-7ccf-46ce-8227-24cce9be812e | test@test.com | 2024-02-08 21:26:30.372286 | 2024-02-09 21:26:30.37229 | editor |
2 | user_admin_1 | 65cfd915-240c-4f64-8378-27fa1ff9cdf5 | test1@test.com | 2024-02-06 21:26:30.372286 | 2024-02-07 21:26:30.37229 | editor |
3 | user_admin_2 | 9b4dca2e-f1d2-4b14-9553-3b8913323b48 | test2@test.com | 2024-02-04 21:26:30.372286 | 2024-02-06 21:26:30.37229 | editor |
3. Query Derivation
Query derivation allows the developer to define method names in the repository interface that follow a naming convention, and the framework generates an appropriate query based on that method name.
For example, if we want to search the accounts table by email address, then our method in AccountRepository would look like below:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByEmail(String email);
}
If we execute findByEmail() on AccountRepository, Spring Data generates the SQL below and executes it against the accounts table:
select a1_0.user_id,a1_0.created_on,a1_0.email,a1_0.last_login,a1_0.password,a1_0.permissions_id,a1_0.username
from accounts a1_0
where a1_0.email=?
Our test verifies the working of findByEmail() :
@Test
void givenAccountInDb_whenPerformFindByEmail_thenReturnsAccount() {
String email = "test@test.com";
Account account = accountRepository.findByEmail(email);
assertThat(account.getEmail()).isEqualTo(email);
}
4. findBy() With Multiple Columns
We can extend the query derivation feature to add a combination of conditions to get an appropriate result.
Let’s use the AccountRepository interface and write another method for finding Accounts with usernames and emails:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByUsernameAndEmail(String username, String email);
}
Here’s the generated SQL for the defined method:
select a1_0.user_id,a1_0.created_on,a1_0.email,a1_0.last_login,a1_0.password,a1_0.permissions_id,a1_0.username
from accounts a1_0
where a1_0.username=? and a1_0.email=?
Our test verifies the working of findByUsernameAndEmail():
@Test
void givenAccountInDb_whenPerformFindByUsernameAndEmail_thenReturnsAccount(){
String email = "test@test.com";
String username = "user_admin";
Account account = accountRepository.findByUsernameAndEmail(username, email);
assertThat(account.getUsername()).isEqualTo(username);
assertThat(account.getEmail()).isEqualTo(email);
}
We can also use the OR operator to combine two conditions. For example, we can search by either username or email:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByUsernameOrEmail(String username, String email);
}
Let’s see the generated SQL:
select a1_0.user_id,a1_0.created_on,a1_0.email,a1_0.last_login,a1_0.password,a1_0.permissions_id,a1_0.username
from accounts a1_0
where a1_0.username=? or a1_0.email=?
Now, let’s verify the working of findByUsernameOrEmail():
@Test
void givenAccountInDb_whenPerformFindByUsernameOrEmail_thenReturnsAccount(){
String email = "test@test.com";
String username = "user_editor";
Account account = accountRepository.findByUsernameOrEmail(username, email);
assertThat(account.getUsername()).isNotEqualTo(username);
assertThat(account.getEmail()).isEqualTo(email);
}
We can use the collection of input in findBy() method. For example, to find all accounts that exist in the list of emails or list of usernames, we can write a method in AccountRepository:
public interface AccountRepository extends JpaRepository<Account, Integer> {
List<Account> findByUsernameInOrEmailIn(List<String> usernames, List<String> emails);
}
Let’s check out the generated SQL:
select a1_0.user_id,a1_0.created_on,a1_0.email,a1_0.last_login,a1_0.password,a1_0.permissions_id,a1_0.username
from accounts a1_0
where a1_0.username in (?,?) or a1_0.email in (?,?,?)
Our test confirms the working of findByUsernameInOrEmailIn():
@Test
void givenAccountInDb_whenPerformFindByUsernameInOrEmailIn_thenReturnsAccounts(){
List<String> emails = List.of("test@test.com", "abc@abc.com", "pqr@pqr.com");
List<String> usernames = List.of("user_editor", "user_admin");
List<Account> byUsernameInOrEmailIn = accountRepository.findByUsernameInOrEmailIn(usernames, emails);
assertThat(byUsernameInOrEmailIn.size()).isEqualTo(1);
assertThat(byUsernameInOrEmailIn.get(0).getEmail()).isEqualTo("test@test.com");
}
5. Conclusion
In this tutorial, we discussed the query derivation feature of Spring Data and used it to find entities in a table. We also explored the usage of various input parameters for finding entities with conditions such as AND and OR.
As always, the example code is available over on GitHub.