> For the complete documentation index, see [llms.txt](https://spring-boot.shujuwajue.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://spring-boot.shujuwajue.com/guan-xi-xing-shu-ju-ku/spring-data-jpa/cha-xun-3001-fen-ye-3001-pai-xu.md).

# 查询、分页、排序

## 查询、分页、排序

## 查询

查询有

* 方法名字方式查询
* @Query注解方式查询
* 动态SQL方式查询
* Example方式查询

JpaJpaRepository提供了如下表所述的内置查询。

* `List<T> findAll();` - 返回所有实体
* `List<T> findAllById(Iterable<ID> var1);` - 返回指定id的所有实体
* `T getOne(ID var1);` - 根据id返回对应的实体，如果未找到，则返回空。
* `List<T> findAll(Sort var1);` - 返回所有实体，按照指定顺序返回。
* `Page<T> findAll(Pageable var1);` - 返回实体列表，实体的offset 和limit通过pageable来指定

#### 方法名字方式查询方式

Spring Data 通过查询的方法名和参数名来自动构造一个JPA QQL查询

比如

```java
public interface UserRepository extends JpaRepository<User, Integer> {
    public User findByName(String name);
}
```

方法名和参数名要遵守一定的规则， Spring Data JPA 才能自动转换为JPQL：

* 方法名通常包含多个实体属性用于查询，属性之间可以使用AND和OR连接，也支持Between、LessThan、GreaterThan、Like；
* 方法名可以以findBy、getBy、queryBy 开头；
* 查询结果可以排序，方法名包含OrderBy+属性+ASC（DESC）；
* 可以通过Top、First来限定查询的结果集；
* 一些特殊的参数可以出现在参数列表里,比如Pageeable、Sort

例子：

```java
// 根据名字查询，且按照名字升序
List<Person> findByLastnameOrderByFirstnameAsc(String name);

// 根据名字查询，且使用翻页查询
Page<User> findByLastname(String lastname, Pageable pageable);

// 查询满足条件的前10个用户
List<User> findFirst10ByLastname(String lastname, Sort sort);

// 使用And联合查询
List<Person> findByFirstnameAndLastname(String firstname, String lastname);

// 使用Or查询
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);

// 使用like查询，name 必须包含like中的%或者?
public User findByNameLike(String name);
```

| 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`            | `findByAgeIsNull`                                         | `… 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> age)`                     | `… 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)`                            |

> 注意：Spring Data的Query 适用于关系数据库操作，也适合NOSQL。大部分Spring Boot应用中，Query构造只能创建一些简单的查询。但对于NOSQL来说已经足够了，不需要自己再构造NOSQL查询

#### @Query注解方式查询

注解Query允许在方法上使用JPQL

> 其中操作针对的是对象名和对象属性名，而非数据库中的表名和字段名

```java
@Query("select u form User u where u.name=?1 and u.depantment.id=?2");
public User findUser(String name, Integer departmentId);
```

```java
@Query("form User u where u.name=?1 and u.depantment.id=?2");
public User findUser(String name, Integer departmentId);
```

如果使用SQL而不是JPSQL，可以使用`nativeQuery`属性，设置为true

```java
@Query(value="select * from user where name=?1 and department_id=?2", nativeQuery=true)
public User nativeQuery(String name, Integer departmentId);
```

无论JPQL，还是SQL，都支持"命名参数"：

```java
@Query(value="select * from user where name=:name and department_id=:departmentId", nativeQuery=true)
public User nativeQuery2(String name, Integer departmentId);
```

如果SQL活着JPQL查询结果集并非Entity，可以用Object\[]数组代替，比如分组统计每个部分的用户数：

```java
@Query(value="select department_id,count(*) from user group by department_id", nativeQuery=true)
public List<Object[]> queryUserCount()
```

这条查询将返回数组，对象类型依赖于查询结果，被示例中，返回的是String和BigInteger类型

查询时可以使用Pageable和Sort来完成翻页和排序。

```java
@Query("select u from User u where department.id=?1")
public Page<User> QueryUsers(Integer departmentId, Pageable page);
```

@Query 还允许SQL更新、删除语句，此时必须搭配@Modifying使用，比如：

```java
@Modifying
@Query("update User u set u.name= ?1 where u.id= ?2")
int updateName(String name, Integer id);
```

#### 动态SQL方式查询

[spring data jpa 利用JpaSpecificationExecutor做复杂查询](https://blog.csdn.net/yingxiake/article/details/51014223)

[Spring data JPA中使用Specifications动态构建查询](https://www.cnblogs.com/happyday56/p/4661839.html)

#### Example方式查询

允许根据实体创建一个Example对象，Spring Data通过Example对象来构造JPQL。但是使用不灵活条件是AND,不能使用or，时间的大于小于，between等。

> 继承`JpaRepository`
>
> ```java
> <S extends T> List<S> findAll(Example<S> var1);
>
> <S extends T> List<S> findAll(Example<S> var1, Sort var2);
> ```

```java
public List<User> getByExample(String name) {
    Department dept = new Department();
    dept.setId(1);

    User user = new User();
    user.setName(name);
    user.setDepartment(dept);
    Example<User> example = Example.of(user);
    List<User> list = userDao.findAll(example);
    return list
}
```

以上代码首先创建了User对象，设置 查询条件，名称为参数name，部门id为1，通过`Example.of`构造了此查询。

大部分查询并非完全匹配查询，ExampleMatcher 提供了更多的条件指定.比如以xxx开头的所有用户，则可以使用以下代码构造：

```java
ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("xxx",
    GenericPropertyMatchers.startsWith().ignoreCase());
Example<User> example = Example.of(user, matcher);
```

资料：[Spring Data JPA 实例查询](https://www.cnblogs.com/rulian/p/6533109.html)

### 排序Sort

Sort对象用来指定排序，最简单的Sort对象构造可以传入一个属性名列表（不是数据库列名，是属性名）。默认采用升序排序。

```java
Sort sort = new Sort("id");
//Sort sort = new Sort(Direction.DESC, "id");
return userDao.findAll(sort);
```

Hibernate 根据Sort构造了排序条件，`Sort("id")`表示按照id采用默认 升序进行排序

其他Sort的构造方法还包括以下主要的一些：

* `public Sort(String... properties)`, 按照指定的属性列表升序排序。
* `public Sort(Sort.Direction direction, String... properties)`, 按照指定属性列表排序，排序由`direction`指定，`direction`是一个枚举类型，有`Direction.ASC` 和 `Direction.DESC`
* `public Sort(Sort.Order... orders)`, 可以通过Order静态方法来创建
  * public static Sort.Order asc(String property)
  * public static Sort.Order desc(String property)

### 分页`Page和Pageable`

`Pageable 接口用于构造翻页查询，PageRequest是其实现类，可以通过提供的工厂方法创建PageRequest：`

> 注意我这边使用的是sring boot 2.0.2 ，jpa版本是2.0.8，新版本与之前版本的操作方法有所不同。

* `public static PageRequest of(int page, int size)`
* `public static PageRequest of(int page, int size, Sort sort) - 也可以在PageRequest中加入排序`
* `public static PageRequest of(int page, int size, Direction direction, String... properties)`,或者自定义排序规则

`page是从0开始，表示查询页，size指每页的期望行数。`

Spring Data翻页查询总是返回Page对象，Page对象提供了以下常用的方法

* `int getTotalPages();`, 总的页数
* `long getTotalElements();` - 返回总数
* `List<T> getContent();` - 返回此次查询的结果集

示例：

```java
Public List<User> getAllUsers(int page, int size) {
    PageRequest pageable = PageRequest.of(page, size);
    Page<User> pageObject = userDao.findAll(pageable);
    int totalPage = pageObject.getTotalPages();
    int count = pageObject.getTotalElements();
    return pageObject.getContent()
}
```
