# 查询、分页、排序

## 查询、分页、排序

## 查询

查询有

* 方法名字方式查询
* @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()
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://spring-boot.shujuwajue.com/guan-xi-xing-shu-ju-ku/spring-data-jpa/cha-xun-3001-fen-ye-3001-pai-xu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
