本文章参考文档为《SpringDataJpa从入门到精通》
我们看一下JpaRepository的UML类图
我们可以看到, 自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接口为我们提供了什么样的匹配方式
这些以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的限制条件)