В предыдущей статье было представлено базовое построение и интеграция jpa данных Spring в Spring Boot, а также относительно простые запросы В этой статье описано, как реализовать более сложные запросы, разбиение на страницы и сортировку в jpa данных Spring.
1 Спецификация, полученная с помощью сложного запроса
1.1 Что такое спецификация
Спецификация — это интерфейс в springDateJpa, расширение некоторых базовых операций CRUD для jpa, его можно понимать как сложный интерфейс запросов в spring jpa. Во-вторых, нам нужно понять запрос Criteria, который является типобезопасным и более объектно-ориентированным запросом. Spring Data JPA поддерживает запрос критериев JPA2.0, а соответствующий интерфейс — JpaSpecificationExecutor.
Интерфейс JpaSpecificationExecutor в основном определяется интерфейсом Specification, который определяет только следующий метод:
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
Основные понятия запроса критериев
Критериальные запросы основаны на концепции метамодели, которая определяется для управляемых сущностей определенной единицы сохраняемости, которые могут быть классами сущностей, встроенными классами или сопоставленными родительскими классами.
Интерфейс CriteriaQuery
Представляет конкретный объект запроса верхнего уровня, который содержит различные части запроса, такие как: выбрать, откуда, где, сгруппировать по, упорядочить по и т. д. Примечание. Объект CriteriaQuery работает только с типом сущности или запросом критериев встроенного типа.
Root:
Представляет корневой объект запроса "Критерии". Корень запроса "Критерии" определяет тип объекта, который можно использовать для получения желаемых результатов для будущей навигации. Он аналогичен предложению FROM в запросе SQL.
Корневые экземпляры типизированы и определяют типы, которые могут появляться в предложении FROM запроса. root представляет класс сущности запроса, запрос может получить из него корневой объект, сообщить jpa, какой класс сущности запрашивать, вы также можете добавить условия запроса, и вы можете объединить объект EntityManager, чтобы получить объект TypedQuery окончательного запроса.Интерфейс CriteriaBuilder
Объект построителя Predicate, используемый для построения CritiaQuery: простой или сложный тип предиката, фактически эквивалентный условию или комбинации условий. Доступно через EntityManager.getCriteriaBuilder.
2 Используйте спецификацию для сложных динамических запросов
Зависимости Maven можно продолжить использовать в предыдущей главе.Здесь измените класс сущности и уровень контроллера.
Класс сущности запроса:
@Data
public class AccountRequest {
//从第几页开始
private Integer page;
//每一页查询多少
private Integer limit;
private String id;
private String name;
private String pwd;
private String email;
private Integer[] types;
}
Класс сущности:
@Data
@Entity
@Table(name = "account")
@ToString
@EntityListeners(AuditingEntityListener.class)
public class Account {
@Id
@GenericGenerator(name = "idGenerator", strategy = "uuid")
@GeneratedValue(generator = "idGenerator")
private String id;
@Column(name = "username", unique = true, nullable = false, length = 64)
private String username;
@Column(name = "password", nullable = false, length = 64)
private String password;
@Column(name = "email", length = 64)
private String email;
@Column(name = "type")
private Short type;
@CreatedDate
@Column(name = "create_time", nullable = false)
private LocalDateTime createTime;
}
Слой репозитория:
public interface AccountRepository extends JpaRepository<Account,String>, JpaSpecificationExecutor<Account> {}
Уровень контроллера (или пропустить уровень службы напрямую)
@Autowired
private AccountRepository repository;
@PostMapping("/get")
public List<Account> get(@RequestBody AccountRequest request){
Specification<Account> specification = new Specification<Account>() {
@Override
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder builder) {
//所有的断言 及条件
List<Predicate> predicates = new ArrayList<>();
//精确匹配id pwd
if (request.getId() != null) {
predicates.add(builder.equal(root.get("id"), request.getId()));
}
if (request.getPwd() != null) {
predicates.add(builder.equal(root.get("password"), request.getPwd()));
}
//模糊搜索 name
if (request.getName() != null && !request.getName().equals("")) {
predicates.add(builder.like(root.get("username"), "%" + request.getName() + "%"));
}
if (request.getEmail() != null && !request.getEmail().equals("")) {
predicates.add(builder.like(root.get("email"), "%" + request.getEmail() + "%"));
}
//in范围查询
if (request.getTypes() != null) {
CriteriaBuilder.In<Object> types = builder.in(root.get("type"));
for (Integer type : request.getTypes()) {
types = types.value(type);
}
predicates.add(types);
}
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
List<Account> accounts = repository.findAll(specification);
return accounts;
}
Переопределяя метод спецификации toPredicate, такой сложный динамический SQL-запрос завершается, и его можно вызывать непосредственно через почтовый запрос.
3 Пейджинг и сортировка
@PostMapping("/page")
public List<Account> getPage(@RequestBody AccountRequest request){
Specification<Account> specification = new Specification<Account>() {
@Override
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
//do anything
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
//表示通过createTime进行 ASC排序
PageRequest page = new PageRequest(request.getPage() - 1, request.getLimit(), Sort.Direction.ASC, "createTime");
Page<Account> pageInfo = repository.findAll(specification, page);
return pageInfo.getContent();
}
Вышеприведенный код после сложного запроса, разбиения на страницы и сортировки черезPageRequestсоздать правила сортировки страниц. Пройдите на стартовую страницу и количество каждой страницы, а также правило сортировки и атрибут которого для сортировки.Jpa начинается со страницы 0, поэтому нужно быть внимательным при передаче параметров!
Конечно, если вам не нужно выполнять сложные запросы, вы также можете выполнять запросы на разбиение по страницам и сортировку данных.
Измените репозиторий, чтобы он наследовалPagingAndSortingRepository.
@Repository
public interface AccountRepository extends JpaRepository<Account,String>, JpaSpecificationExecutor<Account> , PagingAndSortingRepository<Account,String> {
Page<Account> findByAge(int age, Pageable pageable);
}
Создайте перед использованиемpageableпараметры, а затем передать их.
//显示第1页每页显示3条
PageRequest pr = new PageRequest(1,3);
//根据年龄进行查询
Page<Account> stus = accountPageRepository.findByAge(22,pr);
Сортировка такая же, создайте метод в репозитории
List<Account> findByPwd(String pwd, Sort sort);
При вызове передать объект сортировки
//设置排序方式为username降序
List<Account> accs = accountPageRepository.findByAge("123456",new Sort(Sort.Direction.DESC,"username"));
//设置排序以username和type进行升序
acc = accountPageRepository.findByAge("123456",new Sort(Sort.Direction.ASC,"username","type"));
//设置排序方式以name升序,以address降序
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"name"),new Sort.Order(Sort.Direction.DESC,"type"));
accs = accountPageRepository.findByAge("123456",sort);