1. 概述
Spring Data JPA 提供了一个查询推导功能,允许开发者通过遵循方法名称约定,自动衍生查询语句。
本文将利用这个特性,通过单个或多个列查找实体。
2. 示例设置
以一个包含用户账户相关属性的 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
}
为了演示,我们将在 Accounts
表中添加一些示例数据:
userId | username | password | createdOn | lastLogin | permission | |
---|---|---|---|---|---|---|
1 | user_admin | 737d4251-7ccf-46ce-8227-24cce9be812e | [email protected] | 2024-02-08 21:26:30.372286 | 2024-02-09 21:26:30.37229 | editor |
2 | user_admin_1 | 65cfd915-240c-4f64-8378-27fa1ff9cdf5 | [email protected] | 2024-02-06 21:26:30.372286 | 2024-02-07 21:26:30.37229 | editor |
3 | user_admin_2 | 9b4dca2e-f1d2-4b14-9553-3b8913323b48 | [email protected] | 2024-02-04 21:26:30.372286 | 2024-02-06 21:26:30.37229 | editor |
3. 查询推导
查询推导允许开发者在仓库接口中定义遵循特定命名约定的方法,框架会根据方法名自动生成相应的查询。
例如,如果我们想通过电子邮件地址搜索 accounts
表,AccountRepository
中的方法如下:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByEmail(String email);
}
当我们在 AccountRepository
上执行 findByEmail()
时,Spring Data 会生成以下 SQL 并执行到 accounts
表:
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=?
我们的测试验证了 findByEmail()
的工作:
@Test
void givenAccountInDb_whenPerformFindByEmail_thenReturnsAccount() {
String email = "[email protected]";
Account account = accountRepository.findByEmail(email);
assertThat(account.getEmail()).isEqualTo(email);
}
4. 多列查询 (findBy*()
) 方法
我们可以扩展查询推导功能,添加组合条件来获取合适的结果。
使用 AccountRepository
接口,我们再编写一个方法,用于根据用户名和电子邮件查找 Account
:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByUsernameAndEmail(String username, String email);
}
定义的方法生成的 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=? and a1_0.email=?
测试确认了 findByUsernameAndEmail()
的工作:
@Test
void givenAccountInDb_whenPerformFindByUsernameAndEmail_thenReturnsAccount(){
String email = "[email protected]";
String username = "user_admin";
Account account = accountRepository.findByUsernameAndEmail(username, email);
assertThat(account.getUsername()).isEqualTo(username);
assertThat(account.getEmail()).isEqualTo(email);
}
我们也可以使用 OR
运算符组合两个条件。例如,可以通过用户名或电子邮件进行搜索:
public interface AccountRepository extends JpaRepository<Account, Integer> {
Account findByUsernameOrEmail(String username, String email);
}
对应的生成的 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=?
现在,让我们验证 findByUsernameOrEmail()
的工作:
@Test
void givenAccountInDb_whenPerformFindByUsernameOrEmail_thenReturnsAccount(){
String email = "[email protected]";
String username = "user_editor";
Account account = accountRepository.findByUsernameOrEmail(username, email);
assertThat(account.getUsername()).isNotEqualTo(username);
assertThat(account.getEmail()).isEqualTo(email);
}
我们还可以在 findBy()
方法中使用集合作为输入。例如,查找存在于电子邮件列表或用户名列表中的所有账户:
public interface AccountRepository extends JpaRepository<Account, Integer> {
List<Account> findByUsernameInOrEmailIn(List<String> usernames, List<String> emails);
}
生成的 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 (?,?,?)
测试确认了 findByUsernameInOrEmailIn()
的工作:
@Test
void givenAccountInDb_whenPerformFindByUsernameInOrEmailIn_thenReturnsAccounts(){
List<String> emails = List.of("[email protected]", "[email protected]", "[email protected]");
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("[email protected]");
}
5. 总结
本教程讨论了 Spring Data 的查询推导功能,并用它来在表中查找实体。我们还探讨了如何使用不同的输入参数,根据 AND
和 OR
条件找到实体。
如往常一样,示例代码可在 GitHub 查看。