Что такое query java
Spring Data JPA
В статье опишу использование Spring Data.
Spring Data — дополнительный удобный механизм для взаимодействия с сущностями базы данных, организации их в репозитории, извлечение данных, изменение, в каких то случаях для этого будет достаточно объявить интерфейс и метод в нем, без имплементации.
1. Spring Repository
Основное понятие в Spring Data — это репозиторий. Это несколько интерфейсов которые используют JPA Entity для взаимодействия с ней. Так например интерфейс
public interface CrudRepository extends Repository
обеспечивает основные операции по поиску, сохранения, удалению данных (CRUD операции)
Есть и другие абстракции, например PagingAndSortingRepository.
Т.е. если того перечня что предоставляет интерфейс достаточно для взаимодействия с сущностью, то можно прямо расширить базовый интерфейс для своей сущности, дополнить его своими методами запросов и выполнять операции. Сейчас я покажу коротко те шаги что нужны для самого простого случая (не отвлекаясь пока на конфигурации, ORM, базу данных).
1. Создаем сущность
2. Наследоваться от одного из интерфейсов Spring Data, например от CrudRepository
3. Использовать в клиенте (сервисе) новый интерфейс для операций с данными
Здесь я воспользовался готовым методом findById. Т.е. вот так легко и быстро, без имплементации, получим готовый перечень операций из CrudRepository:
Понятно что этого перечня, скорее всего не хватит для взаимодействия с сущностью, и тут можно расширить свой интерфейс дополнительными методами запросов.
2. Методы запросов из имени метода
Запросы к сущности можно строить прямо из имени метода. Для этого используется механизм префиксов find…By, read…By, query…By, count…By, и get…By, далее от префикса метода начинает разбор остальной части. Вводное предложение может содержать дополнительные выражения, например, Distinct. Далее первый By действует как разделитель, чтобы указать начало фактических критериев. Можно определить условия для свойств сущностей и объединить их с помощью And и Or. Примеры
В документации определен весь перечень, и правила написания метода. В качестве результата могут быть сущность T, Optional, List, Stream. В среде разработки, например в Idea, есть подсказка для написания методов запросов.
Достаточно только определить подобным образом метод, без имплементации и Spring подготовит запрос к сущности.
3. Конфигурация и настройка
Весь проект доступен на github
github DemoSpringData
Здесь лишь коснусь некоторых особенностей.
В context.xml определенны бины transactionManager, dataSource и entityManagerFactory. Важно указать в нем также
путь где определены репозитории.
EntityManagerFactory настроен на работу с Hibernate ORM, а он в свою очередь с БД Oracle XE, тут возможны и другие варианты, в context.xml все это видно. В pom файле есть все зависимости.
4. Специальная обработка параметров
В методах запросов, в их параметрах можно использовать специальные параметры Pageable, Sort, а также ограничения Top и First.
5. Пользовательские реализации для репозитория
Предположим что в репозиторие нужен метод, который не получается описать именем метода, тогда можно реализовать с помощью своего интерфейса и класса его имплементирующего. В примере ниже добавлю в репозиторий метод получения сотрудников с максимальной оплатой труда.
Имплементирую интерфейс. С помощью HQL (SQL) получаю сотрудников с максимальной оплатой, возможны и другие реализации.
А также расширяю Crud Repository Employees еще и CustomizedEmployees.
Здесь есть одна важная особенность. Класс имплементирующий интерфейс, должен заканчиваться (postfix) на Impl, или в конфигурации надо поставить свой postfix
Проверяем работу этого метода через репозиторий
Другой случай, когда надо изменить поведение уже существующего метода в интерфейсе Spring, например delete в CrudRepository, мне надо что бы вместо удаления из БД, выставлялся признак удаления. Техника точно такая же. Ниже пример:
Теперь если в employeesCrudRepository вызвать delete, то объект будет только помечен как удаленный.
6. Пользовательский Базовый Репозиторий
В предыдущем примере я показал как переопределить delete в Crud репозитории сущности, но если это надо делать для всех сущностей проекта, делать для каждой свой интерфейс как то не очень. тогда в Spring data можно настроить свой базовый репозиторий. Для этого:
Объявляется интерфейс и в нем метод для переопределения (или общий для всех сущностей проекта). Тут я еще для всех своих сущностей ввел свой интерфейс BaseEntity (это не обязательно), для удобства вызова общих методов, его методы совпадают с методами сущности.
В конфигурации надо указать этот базовый репозиторий, он будет общий для всех репозиториев проекта
Теперь Employees Repository (и др.) надо расширять от BaseRepository и уже его использовать в клиенте.
Проверяю работу EmployeesBaseRepository
Теперь также как и ранее, объект будет помечен как удаленный, и это будет выполняться для всех сущностей, которые расширяют интерфейс BaseRepository. В примере был применен метод поиска — Query by Example (QBE), я не буду здесь его описывать, из примера видно что он делает, просто и удобно.
7. Методы запросов — Query
Ранее я писал, что если нужен специфичный метод или его реализация, которую нельзя описать через имя метода, то это можно сделать через некоторый Customized интерфейс ( CustomizedEmployees) и сделать реализацию вычисления. А можно пойти другим путем, через указание запроса (HQL или SQL), как вычислить данную функцию.
Для моего примера c getEmployeesMaxSalary, этот вариант реализации даже проще. Я еще усложню его входным параметром salary. Т.е. достаточно объявить в интерфейсе метод и запрос вычисления.
Упомяну лишь еще, что запросы могут быть и модифицирующие, для этого к ним добавляется еще аннотация @Modifying
Так например в моем гипотетическом примере, когда мне надо для всех сущностей иметь признак “удален», я сделаю базовый интерфейс с методом получения списка объектов с признаком «удален» или «активный»
Далее все репозитории для сущностей можно расширять от него. Интерфейсы которые не являются репозиториями, но находятся в «base-package» папке конфигурации, надо аннотировать @NoRepositoryBean.
Теперь когда будет выполняться запрос, в тело запроса будет подставлено имя сущности T для конкретного репозитория который будет расширять ParentEntityRepository, в данном случае Employees.
Весенние данные JPA @Query
Узнайте, как использовать аннотацию @Query в Spring Data JPA для определения пользовательских запросов с использованием JPQL и собственного SQL.
1. Обзор
В этом уроке мы продемонстрируем, как использовать аннотацию @Query в Spring Data JPA для выполнения как JPQL, так и собственных SQL-запросов.
Мы также покажем, как построить динамический запрос, когда аннотации @Query недостаточно.
Дальнейшее чтение:
Производные методы запросов в репозиториях Spring Data JPA
Весенние данные JPA @Изменение аннотации
2. Выберите Запрос
Чтобы определить SQL для выполнения для метода хранилища данных Spring, мы можем аннотировать метод с помощью @Query аннотации — его атрибут value содержит JPQL или SQL для выполнения.
Аннотация @Query имеет приоритет над именованными запросами, которые аннотируются с помощью @NamedQuery или определены в orm.xml файл.
Это хороший подход, чтобы поместить определение запроса непосредственно над методом внутри репозитория, а не внутри нашей модели домена в виде именованных запросов. Репозиторий отвечает за сохраняемость, поэтому это лучшее место для хранения этих определений.
2.1. JPQL
По умолчанию в определении запроса используется JPQL.
Давайте рассмотрим простой метод репозитория, который возвращает активные User сущности из базы данных:
2.2. Родной язык
Мы также можем использовать собственный SQL для определения нашего запроса. Все, что нам нужно сделать, это установить значение атрибута NativeQuery в true и определить собственный SQL-запрос в атрибуте value аннотации:
3. Определите порядок в запросе
3.1. Сортировка по предоставленным JPA и производным методам
Для методов, которые мы получаем из коробки, таких как findAll(Sort) или те, которые генерируются путем анализа сигнатур методов, мы можем использовать только свойства объекта для определения нашей сортировки :
Теперь представьте, что мы хотим отсортировать по длине свойства name:
Когда мы выполним приведенный выше код, мы получим исключение:
org.springframework.data.mapping.Исключение PropertyReferenceException: Для типа User не найдена ДЛИНА свойства(имя)!
3.2. JPQL
Когда мы используем JPQL для определения запроса, то данные Spring могут обрабатывать сортировку без каких — либо проблем – все, что нам нужно сделать, это добавить параметр метода типа Sort :
Очень важно, чтобы мы использовали JpaSort.небезопасно() чтобы создать Сортировать экземпляр объекта.
Когда мы используем:
3.3. Родной язык
Если мы это сделаем, мы получим исключение:
org.springframework.data.jpa.repository.запрос.Недопустимое исключение JpaQueryMethod: Невозможно использовать собственные запросы с динамической сортировкой и/или разбиением на страницы
Как говорится в исключении, сортировка не поддерживается для собственных запросов. Сообщение об ошибке дает нам намек на то, что разбиение на страницы также вызовет исключение.
Тем не менее, есть обходной путь, который позволяет разбиение на страницы, и мы рассмотрим его в следующем разделе.
4. Разбиение на страницы
Еще одним преимуществом разбиения на страницы является то, что объем данных, отправляемых с сервера на клиент, сводится к минимуму. Отправляя меньшие фрагменты данных, мы, как правило, видим улучшение производительности.
4.1. JPQL
Использование разбиения на страницы в определении запроса JPQL является простым:
Мы можем пройти PageRequest параметр для получения страницы данных.
Разбиение на страницы также поддерживается для собственных запросов, но требует небольшой дополнительной работы.
4.2. Родной язык
Мы можем включить разбиение на страницы для собственных запросов, объявив дополнительный атрибут countQuery .
Это определяет SQL, который нужно выполнить, чтобы подсчитать количество строк во всем результате:
4.3. Версии Spring Data JPA до версии 2.0.4
Приведенное выше решение для собственных запросов отлично работает для Spring Data JPA версий 2.0.4 и более поздних.
До этой версии, когда мы попытаемся выполнить такой запрос, мы получим то же исключение, которое мы описали в предыдущем разделе о сортировке.
Мы можем преодолеть это, добавив дополнительный параметр для разбиения на страницы внутри нашего запроса:
Мы рассмотрели, как создавать простые запросы select с помощью JPQL и собственного SQL. Далее мы покажем, как определить дополнительные параметры.
5. Параметры индексированного запроса
Существует два возможных способа передачи параметров метода в наш запрос: индексированные и именованные параметры.
В этом разделе мы рассмотрим индексированные параметры.
5.1. JPQL
Для индексированных параметров в JPQL данные Spring будут передавать параметры метода в запрос в том же порядке, в котором они отображаются в объявлении метода :
5.2. Родной язык
Индексированные параметры для собственных запросов работают точно так же, как и для JPQL:
В следующем разделе мы покажем другой подход: передача параметров через имя.
6. Именованные параметры
Мы также можем передать параметры метода в запрос, используя именованные параметры. Мы определяем их с помощью аннотации @Param внутри объявления метода репозитория.
6.1. JPQL
Как упоминалось выше, мы используем аннотацию @Param в объявлении метода для сопоставления параметров, определенных по имени в JPQL, с параметрами из объявления метода:
Обратите внимание, что в приведенном выше примере мы определили, что наши параметры SQL-запроса и метода имеют одинаковые имена, но это не обязательно, если строки значений совпадают:
6.2. Родной язык
Для определения собственного запроса нет никакой разницы в том, как мы передаем параметр через имя в запрос по сравнению с JPQL — мы используем аннотацию @Param :
7. Параметр Сбора
Давайте рассмотрим случай, когда предложение where нашего запроса JPQL или SQL содержит ключевое слово IN (или NOT IN ):
В этом случае мы можем определить метод запроса, который принимает Collection в качестве параметра:
8. Обновите Запросы С Помощью @Modifying
Мы можем использовать аннотацию @ Query для изменения состояния базы данных, также добавив метод @ Modifying annotation в репозиторий.
8.1. JPQL
Метод репозитория, который изменяет данные, имеет два отличия по сравнению с запросом select — он имеет аннотацию @Modifying и, конечно, запрос JPQL использует update вместо select :
Возвращаемое значение определяет, сколько строк было обновлено при выполнении запроса. В запросах обновления можно использовать как индексированные, так и именованные параметры.
8.2. Родной язык
Мы также можем изменить состояние базы данных с помощью собственного запроса. Нам просто нужно добавить аннотацию @Modifying :
8.3. Вставки
9. Динамический запрос
Часто мы сталкиваемся с необходимостью построения операторов SQL на основе условий или наборов данных, значения которых известны только во время выполнения. И в этих случаях мы не можем просто использовать статический запрос.
9.1. Пример динамического запроса
Поскольку набор строится динамически, мы не можем знать во время компиляции, сколько предложений LIKE нужно добавить.
В этом случае мы не можем просто использовать аннотацию @Query , так как мы не можем предоставить статическую инструкцию SQL.
Вместо этого, реализуя пользовательский составной репозиторий, мы можем расширить базовую функциональность JpaRepository и предоставить нашу собственную логику для построения динамического запроса. Давайте посмотрим, как это сделать.
9.2. Пользовательские репозитории и API критериев JPA
Мы начнем с создания пользовательского интерфейса фрагмента:
И тогда мы его реализуем:
Как показано выше, мы использовали API критериев JPA чтобы построить наш динамический запрос.
9.3. Расширение существующего репозитория
Итак, теперь мы интегрируем наш фрагмент, расширив новый интерфейс в UserRepository :
9.4. Использование Репозитория
И, наконец, мы можем вызвать наш метод динамического запроса:
Мы успешно создали составной репозиторий и вызвали наш пользовательский метод.
10. Заключение
Мы также узнали, как реализовать пользовательский репозиторий и создать динамический запрос.
QUERY
Query – основной класс, для работы с БД, через него происходит выборка, запись, обновление и удаление данных. Также можно проводить определенную обработку данных, полученных из БД. Экземпляр класса получаем через фабрику (смотри раздел УСТАНОВКА).
Описание функций
Все функции класса условно можно разбить на 4 группы. Для удобства присвоим каждой группе свой цвет:
Функции all() и aggregator()
Проще всего рассмотреть принцип работы функций на основе примера. В качестве примера возьмём вот такую схему базы данных:
mark (m) | |||
id | student id | lesson id | mark |
---|---|---|---|
1 | 1 | 1 | 3 |
2 | 1 | 1 | 4 |
3 | 1 | 2 | 2 |
4 | 1 | 2 | 2 |
5 | 1 | 2 | 3 |
6 | 1 | 4 | 5 |
7 | 2 | 1 | 2 |
8 | 2 | 1 | 3 |
9 | 2 | 2 | 5 |
10 | 2 | 4 | 4 |
11 | 2 | 3 | 3 |
12 | 2 | 3 | 2 |
13 | 2 | 2 | 5 |
14 | 2 | 3 | 3 |
15 | 2 | 1 | 2 |
16 | 3 | 4 | 2 |
17 | 3 | 1 | 5 |
18 | 3 | 2 | 2 |
19 | 3 | 4 | 3 |
20 | 3 | 3 | 4 |
coursework (c) | |||
id | student id | title | semester number |
---|---|---|---|
1 | 1 | Архитектура и производительность серверных ЦП | 1 |
2 | 2 | Закон единства и борьбы противоположностей | 1 |
3 | 2 | Серверные платформы RISC/UNIX | 2 |
4 | 2 | Вредоносное программное обеспечение | 3 |
5 | 2 | Графическое программирование на Ms Fortran | 4 |
6 | 3 | «Азы» программирования и обучающие программы | 1 |
7 | 3 | История возникновения Интернета | 2 |
student (s) | |||
id | name | semester number | |
---|---|---|---|
1 | Даша | 1 | |
2 | Маша | 4 | |
3 | Паша | 2 |
lesson (l) | |||
id | name | ||
---|---|---|---|
1 | КоМод | ||
2 | Матан | ||
3 | Физ-ра | ||
4 | Философия |
Зачастую при запросе к БД хочется получить не плоский ответ, а древовидный. К примеру, выполнив такой запрос:
Получим плоский результат:
Здесь данные из таблицы student дублируются, а мы бы хотели увидеть такой результат:
Чтобы добиться такого результата, необходимо в функцию all передать массив с опциями:
Рассмотрим более сложный пример. Допустим нам надо получить всех студентов с названием их курсовых и со всеми оценками по всем предметам. Мы бы хотели получить это не в плоском виде, а в древовидном, без дублей. Ниже приведен нужный запрос к БД и результат.
student id | student name | semester number | coursework id | coursework semester | coursework title | lesson id | lesson | mark id | mark |
---|---|---|---|---|---|---|---|---|---|
с таблицы: student | с таблицы: coursework | с таблицы: lesson | с таблицы: mark | ||||||
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 1 | КоМод | 1 | 3 |
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 1 | КоМод | 2 | 4 |
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 2 | Матан | 3 | 2 |
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 2 | Матан | 4 | 2 |
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 2 | Матан | 5 | 3 |
1 | Даша | 1 | 1 | 1 | Архитектура и производительность серверных ЦП | 4 | Философия | 6 | 5 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 1 | КоМод | 7 | 2 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 1 | КоМод | 8 | 3 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 1 | КоМод | 15 | 2 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 2 | Матан | 9 | 5 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 2 | Матан | 13 | 5 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 3 | Физ-ра | 11 | 3 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 3 | Физ-ра | 12 | 2 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 3 | Физ-ра | 14 | 3 |
2 | Маша | 4 | 2 | 1 | Закон единства и борьбы противоположностей | 4 | Философия | 10 | 4 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 1 | КоМод | 7 | 2 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 1 | КоМод | 8 | 3 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 1 | КоМод | 15 | 2 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 2 | Матан | 9 | 5 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 2 | Матан | 13 | 5 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 3 | Физ-ра | 11 | 3 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 3 | Физ-ра | 12 | 2 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 3 | Физ-ра | 14 | 3 |
2 | Маша | 4 | 3 | 2 | Серверные платформы RISC/UNIX | 4 | Философия | 10 | 4 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 1 | КоМод | 7 | 2 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 1 | КоМод | 8 | 3 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 1 | КоМод | 15 | 2 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 2 | Матан | 9 | 5 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 2 | Матан | 13 | 5 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 3 | Физ-ра | 11 | 3 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 3 | Физ-ра | 12 | 2 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 3 | Физ-ра | 14 | 3 |
2 | Маша | 4 | 4 | 3 | Вредоносное программное обеспечение | 4 | Философия | 10 | 4 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 1 | КоМод | 7 | 2 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 1 | КоМод | 8 | 3 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 1 | КоМод | 15 | 2 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 2 | Матан | 9 | 5 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 2 | Матан | 13 | 5 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 3 | Физ-ра | 11 | 3 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 3 | Физ-ра | 12 | 2 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 3 | Физ-ра | 14 | 3 |
2 | Маша | 4 | 5 | 4 | Графическое программирование на Ms Fortran | 4 | Философия | 10 | 4 |
3 | Паша | 2 | 6 | 1 | «Азы» программирования и обучающие программы | 1 | КоМод | 17 | 5 |
3 | Паша | 2 | 6 | 1 | «Азы» программирования и обучающие программы | 2 | Матан | 18 | 2 |
3 | Паша | 2 | 6 | 1 | «Азы» программирования и обучающие программы | 3 | Физ-ра | 20 | 4 |
3 | Паша | 2 | 6 | 1 | «Азы» программирования и обучающие программы | 4 | Философия | 16 | 2 |
3 | Паша | 2 | 6 | 1 | «Азы» программирования и обучающие программы | 4 | Философия | 19 | 3 |
3 | Паша | 2 | 7 | 2 | История возникновения Интернета | 1 | КоМод | 17 | 5 |
3 | Паша | 2 | 7 | 2 | История возникновения Интернета | 2 | Матан | 18 | 2 |
3 | Паша | 2 | 7 | 2 | История возникновения Интернета | 3 | Физ-ра | 20 | 4 |
3 | Паша | 2 | 7 | 2 | История возникновения Интернета | 4 | Философия | 16 | 2 |
3 | Паша | 2 | 7 | 2 | История возникновения Интернета | 4 | Философия | 19 | 3 |
Пагинация с агрегированным запросом считает только верхние, самые основные данные. В приведенном выше примере для пагинации будет только 3 строчки.
whereWithJoin()
$options рассмотрим на примере:
Внутри одной скобки псевдонимы будут одинаковые (использована одна таблица). Ниже в примерах это видно.
Бывает необходимо написать запрос в рамках одной таблицы с поиском по нескольким столбцам, причем условие по нескольким столбцам описывает одну сущность (со связью И), а нам нужно поискать несколько сущностей. К примеру в таблице t есть две колонки cl1 и cl2. Составим запрос на псевдокоде:
Данный запрос вернет нам пустую выборку, для корректной работы необходимо таблицу t сджойнить, это уже будет таблица t ‘, тогда правильный запрос будет:
На практике это может понадобиться при поиске по динамическим свойствам динамических объектов. Давайте рассмотрим этот вариант на примере.
Сделаем упрощенный, демонстрационный вариант. У нас есть таблица product, куда записываются новые, поступающие товары. У нее будет 2 колонки: тип товара и id. Так же будет табличка properties, где будут хранится все свойства товаров. В табличке val будут хранится id товара, id свойств этого товара и значения этих свойств. Вот содержимое таблиц:
К примеру пользователь на фронтенде сформировал следующее условие:
«Получить все товары у которых лошадиных сил меньше 100 или диагональ экрана меньше 6 дюймов. Также товары должны весить меньше тонны и быть в эксплуатации больше месяца.»
Как видим из результата запроса, мы получили 18 записей, хотя по сути тут всего 2 товара, давайте приведем результат к древовидному виду и избавимся от дублей с помощью функции aggregator() или выполнив запрос с массивом настроек агрегации:
Примеры
Пример 1
Необходимо получить список студентов и количество каждой из оценок (сколько 2, 3, 4 и 5) по всем предметам. Также нужно использовать постраничное разбиение, показать результаты первой страницы (по 2 записи на странице).
В sorts название столбцов пишутся без префиксов.
Для студентки Даши получить средний бал по всем предметам.
Пример 2
Пользователь должен иметь возможность получить всех студентов, которые подходят под указанные им критерии, а именно пользователь указывает оценки и массив id предметов, у которых должна встречаться такая оценка.
В результате будет сгенерирован примерно следующий sql запрос: