SpringDataJpa系列三之QBE(按照实例对象查询)

本文章参考文档为《SpringDataJpa从入门到精通》

我们看一下JpaRepository的UML类图
SpringDataJpa系列三之QBE(按照实例对象查询)
我们可以看到, 自JpaRepository开始, 就实现了一个名叫QueryByExampleExecutor的接口。 也就是从这开始, SpringData引入对QBE的支持。

什么是QBE?

全称 QueryByExample, 按照示例查询, 通过传入查询示例来生成查询的SQL的技术, 这里面的一个示例即指一个查询的限制条件。 下面是 QueryByExampleExecutor接口为我们提供的QBE的查询方法。

public interface QueryByExampleExecutor<T> {
    <S extends T> Optional<S> findOne(Example<S> var1);

    <S extends T> Iterable<S> findAll(Example<S> var1);

    <S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);

    <S extends T> Page<S> findAll(Example<S> var1, Pageable var2);

    <S extends T> long count(Example<S> var1);

    <S extends T> boolean exists(Example<S> var1);
}

从源码中可以看出, QBE查询技术, 就是通过传入Example对象进行查询(如果需要排序或分页的话, 在需传入Sort和Pageable的实例)。 故我们的学习也应该从Example 类开始。

怎么理解Example

Example我们可以理解成一条查询的限制条件, 通俗的说, 就是 SELECT * FROM 实体类 WHERE 后面拼接的限制条件, 这种把实体对象的封装类Example当做查询条件的方式的优点显而易见, 就是方便, 看得清楚, 使用的简单。 缺点也很清楚, 这种限制条件之间只能用 AND 相连, 不能写 WHERE **** OR **** 的语句。而且你要写个某个字段 between在一个区间的SQL, 也是不行的。 所以这种查询方式也有很大的局限性。

剖析Example类

首先我们先来看一下这个类的创建方法, 可以发现, 也是用工厂方法进行构建实例的一个类。

    static default <T> Example<T> of(T probe) {
        return new TypedExample(probe, ExampleMatcher.matching());
    }

    static default <T> Example<T> of(T probe, ExampleMatcher matcher) {
        return new TypedExample(probe, matcher);
    }

它的构建方式有两种, 区别在于有没有ExampleMatcher实例。 那么probe和matcher又分别代表着什么呢?

  • probe
    是一个与数据库对应的实体类对象, 也代表了你这次QBE想要查询的表是哪一张, 通过向这个实体类赋值, 即可拼接 属性 = ‘value’ 的SQL语句。
  • matcher
    如何使用对象里面的值进行查询, 是一种匹配器, 就比如说, 我可以用这个匹配器来声明, 对象里面的NULL值全都拼入查询条件, 也可以声明, 对于字符串来讲, 忽略大小写。 如果没有传这个参数的话, 会默认帮你创建一个。
    再其次, 我们看一下ExampleMatcher接口为我们提供了什么样的匹配方式
    SpringDataJpa系列三之QBE(按照实例对象查询)
    这些以with开头的方法, 就是我们常用的匹配器。
    举个栗子
/**
     * 通过QBE查询
     *
     * @param name 参数---人的名字
     * @return personList 查询结果
     */
    @PostMapping("/query/{name}")
    @ResponseBody
    public List<Person> queryByExample(@PathVariable String name) {
        // 创建与数据库匹配的实体对象, 并放入查询条件
        Person per = new Person();
        per.setName(name);

        // 创建匹配器, 需要忽略掉基本数据类型
        ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith()).withIgnorePaths("age");
        // 构造Example对象
        Example<Person> example = Example.of(per, matcher);

        List<Person> personList = personJpaRepository.findAll(example);
        return personList;
    }

上面代码的匹配器的意思就是说, name字段我要后模糊查询, 就是是以传入name属性开头的字符串即可。 至于.withIgnorePaths("age")的意思是说, 我们此次匹配忽略age字段, 不把他拼接到限制条件中来。 (特别注意, 基本数据类型性, 就算你不往probe里面赋值, 也会有一个默认值, 例如int类型的会自动拼接等于0的限制条件)