Spring Data JPA-动态查询
Query by Example
介绍
Query by Example (QBE) 是一种用户友好的查询技术,它允许创建动态查询,并且不需要编写包含字段名称的查询。
Query by Example API 由三部分组成:
- Probe:具有填充字段的域对象的实际示例。
-
ExampleMatcher
:ExampleMatcher
包含有关如何匹配特定字段的详细信息。它可以在多个示例中重复使用。 -
Example
: 一个Example
包含Probe
和ExampleMatcher
, 它用于创建查询。
Query by Example 的局限性:
- 不支持嵌套或分组的属性约束,例如
firstname = ?0 or (firstname = ?1 and lastname = ?2)
. - 仅支持字符串的开始/包含/结束/正则表达式匹配和其他属性类型的精确匹配。
使用
在开始使用 Query by Example 之前,您需要有一个domain对象。
示例:
@Entity
@Table(name = "tb_customer")
@Data
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long customerId; //客户的主键
@Column(name = "name")
private String customerName;//客户名称
@Column(name = "address")
private String customerAddress;//客户地址
}
上面的domain对象,您可以使用它来创建一个Example
。默认情况下,忽略具有null
值的字段,空字符串不会忽略。
您可以使用repositories运行示例查询,只需要将repository接口继承QueryByExampleExecutor<T>
就可以,示例:
public interface CustomerRepository extends JpaRepository<Customer, Long>, QueryByExampleExecutor<Customer> {
}
运行示例:
/**
* Description: 实现客户名称、客户地址动态查询
*
* @author: ZhuMaoqiang
* @date: 2022-03-20 20:27
*/
@Test
public void test01() {
/*
* 查询条件
* 默认情况下,如果属性的值是null则该属性不进行自动组装查询条件
* 如果是空字符串也会进行组装
*/
Customer customer = new Customer();
customer.setCustomerName("朱茂强");
customer.setCustomerAddress("qingdao");
/*
* 通过Example构建查询条件
* 注意选择的Example是SpringData中的Example
*/
Example<Customer> example = Example.of(customer);
List<Customer> list = (List<Customer>) repository.findAll(example);
}
ExampleMatcher
有的时候我们不仅仅只希望动态拼接查询,我们通常有更复杂的需求,比如按照客户姓名左模糊插叙,忽略大小写查询等,这些都可以通过
ExampleMatcher
来实现。
自定义匹配的示例:
/**
* Description: 实现客户名称、客户地址动态高级查询
*
* @author: ZhuMaoqiang
* @date: 2022-03-20
*/
@Test
public void test02() {
// 查询条件
Customer customer = new Customer();
customer.setCustomerId(null);
customer.setCustomerName("朱茂强");
customer.setCustomerAddress("QINGDAO");
/*
* 通过匹配器对条件行为进行设置
* 要注意,属性名要和实体类的属性名对应而不是和数据库的字段名对应
* 如果写错了,ExampleMatcher这个东东不会给你检查,只是你配了不起作用
*/
ExampleMatcher matcher = ExampleMatcher.matching()
/*
* 设置允许属性为空值,这样控制字段也会作为查询条件。
* 默认情况下属性值如果为空则不会进行条件的组装
*/
.withIncludeNullValues()
/*
* 设置忽略的属性,该属性不会被自动组装进查询条件
*/
.withIgnorePaths("customerName")
/*
* 设置忽略大小写,如果不指定属性则对所有的属性忽略大小写
*/
.withIgnoreCase("customerAddress")
/*
* 对所有条件中的字符串进行结尾匹配,也就是自动在所有条件中字段值前面添加%
*/
.withStringMatcher(ExampleMatcher.StringMatcher.ENDING);
// 通过Example构建查询条件
Example<Customer> example = Example.of(customer, matcher);
List<Customer> list = (List<Customer>) repository.findAll(example);
}
下表显示了StringMatcher
您可以使用的各种选项,以及在名为firstname
的字段上使用它们的结果:
匹配 | 逻辑结果 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
默认情况下,ExampleMatcher
的设置,处理可以指定字段的,其他的都是对domin中的所有属性都生效。
你可以指定单个属性的行为,例如:
public void test03() {
// 查询条件
Customer customer = new Customer();
customer.setCustomerName("朱茂强");
customer.setCustomerAddress("QINGDAO");
// 通过匹配器 对条件行为进行设置
ExampleMatcher matcher = ExampleMatcher.matching()
/*
* 设置忽略的属性,该属性不会被自动组装进查询条件
*/
.withIgnorePaths("customerName")
/*
* 针对单个条件进行限制, 会使withIgnoreCase失效,需要单独设置。
* 第二种写法:
* .withMatcher("custAddress", ExampleMatcher.GenericPropertyMatchers.endsWith().ignoreCase());
*/
.withMatcher("customerAddress", m -> m.endsWith().ignoreCase());
// 通过Example构建查询条件
Example<Customer> example = Example.of(customer, matcher);
List<Customer> list = (List<Customer>) repository.findAll(example);
}