一、OverView

​ 在上一篇文章中介绍了一下 Spring Data JPA 的一下简单的 CRUD,但是在公司业务中,不可能这么简单;且在业务中大部分都是查询业务,具体点就是动态查询,什么单表多条件查询、多表多条件查询等等……

注:使用环境还是和上一篇一样

二、自定义SQL查询

如果想和这里面写原生的 SQL 语句,只要在 repository 中这样写就行:

1
2
3
@Query(nativeQuery = true,
value = "select * from Student where id = ?1")
Student catchAllById(Long id);

注:

  • 如果要写原生 SQL,@Query 注解中的 nativeQuery 要设置为 true,默认为 false
  • 函数名设置的不能与自带的冲突,例如:findById,如果设置了一样,就会导致使用的自带的 API 进行查询,而不是自定义的,如果真的想使用自定义覆盖自带的,可以在实体类上使用 @NamedQueries 注解

在这里我们就使用 @Test 进行测试:

image-20200911110257151

还有一种使用 JPQL 语法的查询方式:

repository:

1
2
@Query(value = "select s from Student s where s.name = ?1")
List<Student> catchByName(String name);

Test:

image-20200911111044489

关于 JPQL:和在 SQL 中一样,JPQL 中的 select 语句用于执行查询。其语法可表示为:
select_clause form_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

其中:

  1. from 子句是查询语句的必选子句。
  2. select 用来指定查询返回的结果实体或实体的某些属性。
  3. from 子句声明查询源实体类,并指定标识符变量(相当于SQL表的别名)。
  4. 如果不希望返回重复实体,可使用关键字 distinct 修饰。select、from 都是 JPQL 的关键字,通常全大写或全小写,建议不要大小写混用。

在 JPQL 中,查询所有实体的 JPQL 查询语句很简单,如下:
select o from Order o 或 select o from Order as o
这里关键字 as 可以省去,标识符变量的命名规范与 Java 标识符相同,且区分大小写

更多参考:官网文档

三、自定义简单查询

在 Spring Data JPA 的官网也介绍了这种方式,一般这种方式在一些小项目中也经常使用:

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull, Null findByAge(Is)Null … where x.age is null
IsNotNull, NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

原理大概如下:

假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析

  1. 先判断 userDepUuid (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
  2. 从右往左截取第一个大写字母开头的字符串此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设user为查询实体的一个属性;
  3. 接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据 Doc.user.depUuid 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 Doc.user.dep.uuid 的值进行查询。
  4. 可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以明确在属性之间加上 “_” 以显式表达意图,比如 findByUser_DepUuid() 或者 findByUserDep_uuid()

repository:

1
2
3
4
5
6
7
8
9
10
11
List<Student> findByName(String name);

List<Student> findByNameAndPwd(String name, String pwd);

List<Student> findByNameStartingWith(String name);

List<Student> findByNameEndingWith(String name);

List<Student> findByNameContaining(String name);

List<Student> findByNameContainingOrderByIdDesc(String name);

test:

findByName

image-20200911112401002

findByNameAndPwd

image-20200911112633532

findByNameStartingWith

image-20200911112706325

findByNameEndingWith

image-20200911112749472

findByNameContaining

image-20200911112818300

findByNameContainingOrderByIdDesc

image-20200911120538084

四、分页查询

1
2
3
4
5
6
7
8
9
10
// 分页 + 排序
@Test
public void find6() {
Sort sort = Sort.by(Sort.Direction.DESC, "id");
PageRequest pageRequest = PageRequest.of(0, 2, sort);
Page<Student> studentPage = studentRepository.findAll(pageRequest);
for (Student student : studentPage.getContent()) {
System.out.println(student);
}
}

注:

  • Sort.by(排序方式, 排序字段)
  • PageRequest.of(第多少页,页大小) ——–> 一般是写一个 PageUtils

Test

image-20200911142717361

五、动态查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 分页 + 排序 + 搜索
@Test
public void find7() {
PageRequest pageRequest = PageRequest.of(0, 2, Sort.by(Sort.Direction.DESC, "id"));
Specification<Student> specification = new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// 增加搜索条件: id <= 5
Predicate predicate = criteriaBuilder.lessThanOrEqualTo(root.get("id"), 5);
return predicate;
}
};
Page<Student> studentPage = studentRepository.findAll(specification, pageRequest);
for (Student student : studentPage.getContent()) {
System.out.println(student);
}
}

注:

  • root: 代表查询的实体类

  • query: 可以从中得到 Root 对象

  • criteriaBuilder: 用于创建 Criteria 相关对象的工厂

  • Predicate: 代表一个查询条件

Test:

image-20200911143430602

多条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 分页 + 排序 + 搜索
@Test
public void find7() {
PageRequest pageRequest = PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "id"));
List<Predicate> predicateList = new ArrayList<>();
Specification<Student> specification = new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
predicateList.add(criteriaBuilder.lessThanOrEqualTo(root.get("id"), 5));
predicateList.add(criteriaBuilder.like(root.get("name"), "%" + "er"));
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}
};
Page<Student> studentPage = studentRepository.findAll(specification, pageRequest);
for (Student student : studentPage.getContent()) {
System.out.println(student);
}
}

Test:

image-20200911150134356

评论