Spring Data JPA-动态查询

Spring Data JPA-动态查询

Query by Example

介绍

Query by Example (QBE) 是一种用户友好的查询技术,它允许创建动态查询,并且不需要编写包含字段名称的查询。

Query by Example API 由三部分组成:

  • Probe:具有填充字段的域对象的实际示例。
  • ExampleMatcher:ExampleMatcher包含有关如何匹配特定字段的详细信息。它可以在多个示例中重复使用。
  • Example: 一个Example包含 ProbeExampleMatcher, 它用于创建查询。

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的字段上使用它们的结果:

匹配逻辑结果
DEFAULT(区分大小写)firstname = ?0
DEFAULT(不区分大小写)LOWER(firstname) = LOWER(?0)
EXACT(区分大小写)firstname = ?0
EXACT(不区分大小写)LOWER(firstname) = LOWER(?0)
STARTING(区分大小写)firstname like ?0 + '%'
STARTING(不区分大小写)LOWER(firstname) like LOWER(?0) + '%'
ENDING(区分大小写)firstname like '%' + ?0
ENDING(不区分大小写)LOWER(firstname) like '%' + LOWER(?0)
CONTAINING(区分大小写)firstname like '%' + ?0 + '%'

默认情况下,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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱茂强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值