Query java что это
Перейти к содержимому

Query java что это

  • автор:

Язык запросов HQL

HQL (Hibernate Query Language) – это объекто-ориентированный язык запросов, который очень похож на SQL. Главное различие языков HQL и SQL связано с тем, что SQL формирует запросы из наименований таблиц в базе данных и их столбцов, а HQL работает с сущностями (классами) и их полями (аттрибутами класса).

Hibernate транслирует HQL–запросы в понятные для БД SQL–запросы, которые и выполняют необходимые действия в БД.

Рассмотрим основные ключевые операторы языка HQL :

FROM

Оператор FROM используется для загрузки (чтения) набора объектов. Пример кода :

String hql = "FROM User"; Query query = session.createQuery(hql); List users = query.list();

User представляет POJO класс (User.java), который ассоциирован с таблицей в БД.

WHERE

Оператор WHERE накладывает условие на выборку определенных записей из БД. В следующем коде оператор WHERE используется точно также, как и в обычном SQL :

Query query = session.createQuery("FROM User where name = 'Иван'"); List users = query.list();

Hibernate может использовать оператор WHERE с именованными параметрами (Named Parameters), определяя значение в режиме run-time. Для подстановки соответствующего значения в запрос используется метод setParameter объекта Query, которому в качестве параметров необходимо передать значения :

String hql = "FROM User where name = :paramName"; Query query = session.createQuery(hql); query.setParameter("paramName", "Alex"); List users = query.list();

Запросы HQL не чувствительны к регистру операторов, за исключением названий Java классов и их свойств. Т.е. ‘SeLect’ будет эквивалентен ‘Select’. Но вот с наименованием сущностей/классов и их полями это не проходит; они должны соответствовать описаниям.

INSERT

В HQL поддерживается только форма INSERT INTO . SELECT . , которая имеет серьезные ограничения для вставки записей. С точки зрения SQL данная форма позволяет вставить одну или несколько записей в таблицу из запроса SELECT . . Но это характерно для SQL, а Hibernate (HQL) работает только с сущностями. Попробуем использовать данную форму запроса для вставки записи в Hibernate, и рассмотрим следующий код вставки записи пользователя :

String hql = "insert into User (login, name) " + "select 'oleg', 'Олег' from User"; int rows = session.createQuery (hql).executeUpdate(); System.out.println("rows : " + rows);

В коде выполняется попытка сохранения сущности User со значениями login=’oleg’ и name=’Олег’. Структуру таблицы и описание сущности можно увидеть в примере связанных сущностей, который описан здесь. Выполнение данного кода выведет в консоль следующую информацию :

Hibernate: insert into USERS ( id, login, name ) select SEQ_USER.nextval, 'oleg' as col_0_0_, 'Олег' as col_1_0_ from USERS user0_ rows : 2

Hibernate сформировал запрос insert и вставил 2 записи. Если посмотреть содержимое таблицы БД, то там можно будет увидеть новые две записи. Полагаю, что вторая запись лишняя, но как от нее избавиться. Если вместо наименования сущности User в первой или во второй позиции подставить наименование таблицы Users, то Hibernate вызовет QuerySyntaxException :

Exception in thread "main" \ org.hibernate.hql.internal.ast.QuerySyntaxException: \ Users is not mapped [ \ insert into Users(login, name) \ select 'oleg', 'Олег' from net.common.model.User]

Таким образом, можно заключить, что вставку записей в таблицы при использовании HQL лучше выполнять с применением сущностей и стандартных методов save или saveUpdate объекта сессии, как это представлено в примере.

UPDATE

Ключевое слово UPDATE используется для обновления одного или нескольких полей объекта. При формировании SQL-запроса можно использовать параметры (Named Parameters), рассмотренные выше. Следующий код демонстрирует динамическое определение параметров при обновлении записи. Результат выполнения запроса — количество обновленных (затронутых) записей :

String hql = "update Contact " + "SET firstName = :name " + ", lastName = :lastName " + ", date = :dateParam " + " where query = session.createQuery(hql); query.setParameter("idParam" , 48); query.setParameter("name" , "Киса"); query.setParameter("lastName" , "Воробьянинов"); query.setParameter("dateParam", new Date()); int result = query.executeUpdate();

DELETE

Ключевое слово DELETE используется для удаления одного или нескольких объектов из таблицы. Пример использования :

String hql = "DELETE User WHERE login = :lg"; Query query = session.createQuery(hql); query.setParameter("lg", "oleg"); int rows = query.executeUpdate();

Представленный выше код будет выполнен, если сущность User не имеет связанных объектов, т.е. из таблицы БД будет удалена сущность. Но, если к примеру, User имеет один или несколько связанных объектов Autos, как это представлено в примере связанных сущностей, то сервер БД не сможет удалить запись из таблицы Users, поскольку в таблице Autos имеются «ссылочные» записи. В этом случае Hibernate вызовет org.hibernate.exception.ConstraintViolationException : could not execute statement.

Для удаления связанных сущностей необходимо использовать объект сессии Session. Следующий код демонстрирует удаление пользователя User, у которого имеются связанные объекты (сущности):

User user = session.load(User.class, 50); if (user != null)

В консоль будут выведены следующие сформированные Hibernate запросы удаления сущности User и связанных с ней сущностей Auto :

Hibernate: delete from autos where aid=? Hibernate: delete from autos where aid=? Hibernate: delete from USERS where >Обратите внимание, что дочерние/связанные сущности удаляются в первую очередь.

Использование алиаса AS

Оператор AS можно использовать в качестве алиаcа в HQL запросе, особенно, если запрос получается длинным. Следующий код демонстрирует определение алиаса пользователя User как ‘u’ (без его использования в запросе) :

String hql = "FROM User AS u"; Query query = session.createQuery(hql); List results = query.list();

Оператор AS можно не включать в запрос, и сразу же после наименования сущности определить ее алиас :

String hql = "FROM User u"; Query query = session.createQuery(hql); List results = query.list();

Ниже представлены примеры кода с использованием алиаса.

GROUP BY

При использовании в HQL агрегатных функций необходимо выполнять группировку по определенным в запросе полям, аналогично SQL. Следующий код демонстрирует использование агрегатной функции SUM с группировкой по полю firstName сущности Employee :

String hql = "SELECT SUM (e.salary), e.firtName " + "FROM Employee e " + "GROUP BY e.firstName"; Query query = session.createQuery(hql); List results = query.list();

Агрегатные функции HQL

В таблице представлены агрегатные функции, поддерживаемые HQL :

Функция Описание
avg (property name) получение среднего значения
count (property name or *) получение количества записей
max (property name) получение максимального значения
min (property name) получение минимального значения
sum (property name) вычисление суммарного значения

Использование оператора DISTINCT позволяет выделить уникальные значения. Следующий запрос вернет количество уникальных имен сотрудников :

String hql = "SELECT COUNT (distinct e.firstName) " + "FROM Employee e"; Query query = session.createQuery(hql); List results = query.list();

ORDER BY

Сортировка результатов запроса в HQL выполняется с использованием ORDER BY аналогично SQL. Сортировать значения можно как по возрастанию (ASC ascending), так и по убыванию (DESC descending). Следующий код демонстрирует чтение сотрудников с сортировкой по убыванию :

String hql = "FROM Employee e " + "WHERE e.id > 10 ORDER BY e.salary DESC"; Query query = session.createQuery(hql); List results = query.list();

Если необходимо выполнить сортировку более чем по одному из полей, то можно после оператора ORDER BY указать список полей с порядком сортировки, разделенных запятой :

String hql = "FROM Employee e " + "WHERE e.id > 10 " + "ORDER BY e.firstName DESC, e.salary DESC"; Query query = session.createQuery(hql); List results = query.list();

Разбиение на страницы

Объект Query включает два метода, позволяющие организовать разбиение данных по-страницам :

  • setFirstResult (int start) — параметр start определяет первую извлекаемую запись в запросе (отсчет от 0);
  • setMaxResults (int max) — параметр max определяет количество извлекаемых записей в результирующем запросе.

Используя вышеописанные методы можно организовать постраничное представление набора данных. Следующий пример выбирает из БД 8 сотрудников, начиная с 5-ой записи :

String hql = "FROM Employee"; Query query = session.createQuery(hql); query.setFirstResult(5); query.setMaxResults (8); List results = query.list();

Класс Query

Метод для поиска документа на основании сформированной выборки.

Параметр Тип Свойства Описание Пример значения
callback CallbackFindDocument Обязательный Callback, который будет вызван после выполнения запроса.

Если вы хотите чтобы вместе с id найденных документов возвращались их поля, задайте название необходимых полей с помощью функции setFieldsForSearch(fields)

Данный метод по-умолчанию возвращает не более 50 документов коллекции.

Пример

Query query = new Query(“mycollection”) .equalTo("number3", 10) .exists("number2"); query.findDocuments(new CallbackFindDocument() < @Override public void onDocumentFound(ListdocumentInfos) < //found. See document list what match query >@Override public void onDocumentNotFound(String errorCode, String errorMessage) < //no documents what match query >>);

.countDocuments(callback)

Метод для подсчета документов из сформированной выборки.

Параметр Тип Свойства Описание Пример значения
callback CallbackCountDocument Обязательный Callback, который будет вызван после выполнения запроса.

Пример

Query query = new Query(“mycollection”); query.greaterThan("rating", 10); query.countDocuments(new CallbackCountDocument() < @Override public void onDocumentsCounted(ResponseCount responseCount) < //see responseCount.getResult() to find how many documents was found. >@Override public void onCountFailed(String errorCode, String errorMessage) < //error during count >>);

.updateDocument(update, callback)

Метод для обновления документов из сформированной выборки.

Параметр Тип Свойства Описание Пример значения
update Update Обязательный Объект Update
callback CallbackUpdateDocument Обязательный Callback, который будет вызван после выполнения запроса.

Данный метод обновляет не более 1000 документов

Пример

 Query query = new Query(“mycollection”); query.equalTo("number3", 10); Update update = new Update() .set("number2", 199) .set("numberField", 111) .addToSet("array1", 900); query.updateDocument(update, new CallbackUpdateDocument() < @Override public void onUpdateSucceed(ResponseUpdate responseUpdate) < //documents updated successful >@Override public void onUpdateFailed(String errorCode, String errorMessage) < //error during update >>);

.removeDocument(callback)

Метод для удаления документов на основании сформированной выборки.

Параметр Тип Свойства Описание Пример значения
callback CallbackRemoveDocument Обязательный Callback, который будет вызван после выполнения запроса.

Данный метод удаляет не более 1000 документов

Пример

Query query = new Query(“mycollection”); query.equalTo("_id", "aJfkipJags"); query.removeDocument(new CallbackRemoveDocument() < @Override public void onRemoveSucceed(ResponseRemove responseRemove) < //succeed. See responseRemove to findout how many documents was removed //and get list of removed documents >@Override public void onRemoveFailed(String errorCode, String errorMessage) < //error during remove operation >>); 

.setLimit(limit)

Метод для установки лимита на количество удаляемых, обновляемых и искомых документов.

Параметр Тип Свойства Описание Пример значения
limit Integer Обязательный Лимит для установки 15

Данный метод принимает 100 в качестве максимального значения limit

Пример

Query query = new Query(“mycollection”); query.setLimit(15); //query.findDocuments(…); 

.setSkip(skip)

Метод для пропуска части объектов перед возвратом результата совершенной выборки

Параметр Тип Свойства Описание Пример значения
skip Integer Обязательный Количество пропускаемых документов 100

Пример

Query query = new Query(“mycollection”); query.setSkip(12); //query.findDocuments(…);

.setPage(page)

Метод для постраничного вывода результатов выборки

Параметр Тип Свойства Описание Пример значения
page Integer Обязательный Номер страницы 2

Пример

Query query = new Query(“mycollection”); //query.setLimit(15); query.setPage(1); //query.findDocuments(…);

.equalTo(field, value)

Метод для добавления условия выборки: только документы для которых значение поля равно значению в условии выборки.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «orderNumber»
value Object Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”); query.equalTo(“orderNumber”, 22); // query.findDocuments(…);

Если вы пытаетесь задать данное условие для поля id документа, помните, что, поскольку id – первичный ключ для документа, вам нужно добавить “_” перед именем поля.

Query query = new Query(“mycollection”); query.equalTo(“_id”, “dasds12dskm”); //query.findDocuments(…);

.notEqualTo(field, value)

Метод для добавления условия выборки: только документы для которых значение поля не равно значению в условии выборки.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «orderNumber»
value Object Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”); query.notEqualTo(“orderNumber”, 22); //query.findDocuments(…);

Если вы пытаетесь задать данное условие для поля id документа, помните, что, поскольку id – первичный ключ для документа, вам нужно добавить “_” перед именем поля.

Query query = new Query(“mycollection”); query.equalTo(“_id”, “dasds12dskm”); //query.findDocuments(…);

.containedIn(field, values)

Метод для добавления условия выборки: только документы у которых значение поля совпадает с одним из значений заданного массива.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «orderNumbers»
value List

Обязательный Массив элементов см.пример ниже

Пример

List numbers = new ArrayList<>(); numbers.add(1); numbers.add(5); numbers.add(10); numbers.add(15); Query query = new Query(“mycollection”).containedIn("number3", numbers); //query.findDocuments(…);

.containsAll(field, values)

Метод для добавления условия выборки:только документы у которых поле (типа массив) содержит все элементы заданного массива

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «orderNumbers»
value List

Обязательный Массив элементов см.пример ниже

Пример

List containsAllNumbers = new ArrayList<>(); containsAllNumbers.add(1); containsAllNumbers.add(2); containsAllNumbers.add(3); containsAllNumbers.add(900); Query query = new Query(“mycollection”).containsAll("array1", containsAllNumbers); //query.findDocuments(…);

.notContainedIn(field, values)

Метод для добавления условия выборки: только документы у которых

  • значение поля не совпадает ни с одним из значений заданного массива.
  • значение поля не задано (not exist).

Пример

List notContainsInList = new ArrayList<>(); notContainsInList.add(1); notContainsInList.add(111); notContainsInList.add(11); notContainsInList.add(50); Query query = new Query(“mycollection”).notContainedIn("orderNumbers", notContainsInList) //query.findDocuments(…); 

.greaterThan(field, value)

Метод для добавления условия выборки: только документы у которых значение поля больше указанного в value значения

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
value Integer / Double / Date Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”).greaterThan("number", 22) //query.findDocuments(…);

.greaterThenOrEqualTo(field, value)

Метод для добавления условия выборки: только документы у которых значение поля больше или равно указанного в value значения

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
value Integer / Double / Date Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”).greaterThenOrEqualTo ("number", 22) //query.findDocuments(…);

.lessThan(field, value)

Метод для добавления условия выборки: только документы у которых значение поля меньше указанного в value значения

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
value Integer / Double / Date Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”). lessThan("number", 22) //query.findDocuments(…); 

.lessThanOrEqualTo(field, value)

Метод для добавления условия выборки: только документы у которых значение поля меньше или равно указанного в value значения

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
value Integer / Double / Date Обязательный Значение для сравнения 22

Пример

Query query = new Query(“mycollection”).lessThanOrEqualTo ("number", 22) //query.findDocuments(…);

.exists(field)

Метод для добавления условия выборки: только документы у которых задано значение поля, т.е поле существует и не является пустым.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»

Пример

Query query = new Query(“mycollection”).exists("phoneNumber") //query.findDocuments(…);

.doesNotExist(field)

Метод для добавления условия выборки: только документы у которых не задано значение поля, т.е поле не существует или является пустым.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»

Пример

Query query = new Query(“mycollection”).doesNotExist("phoneNumber") //query.findDocuments(…);

.contains(field, regEx, options)

Метод для добавления условия выборки: только документы у которых значение поля соответствуют заданному регулярному выражению.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
regEx String Обязательный Регулярное выражение “aB”
options RegexOptions Необязательный Опции регулярного выражения см.пример ниже

Пример

 RegexOptions regexOptions = new RegexOptions(); regexOptions.setRegexCaseInsenssitive(); Query query = new Query(“mycollection”).contains("exampleField", "BC", regexOptions) //query.findDocuments(…); 

.startsWith(field, regEx, options)

Метод для добавления условия выборки: только документы у которых значение поля начинается с заданной строки

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
regEx String Обязательный Строка с которой должно начинаться значение поля “aB”
options RegexOptions Необязательный Опции регулярного выражения см.пример ниже

Параметр options позволяет, к примеру, искать все документы начинающиеся с “aB” не зависимо от регистра, т.е искать документы, начинающиеся на ab, aB, Ab, AB.

Пример

 RegexOptions regexOptions = new RegexOptions(); regexOptions.setRegexCaseInsenssitive(); Query query = new Query(“mycollection”).startsWith ("exampleField", "a", regexOptions) //query.findDocuments(…); 

.endsWith(field, regEx, options)

Метод для добавления условия выборки: только документы у которых значение поля заканчивается с заданной строки

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
regEx String Обязательный Строка, на которую должно заканчиваться значение поля “aaB”
options RegexOptions Необязательный Опции регулярного выражения см.пример ниже

Параметр options позволяет, к примеру, искать все документы начинающиеся с “aB” не зависимо от регистра, т.е искать документы, начинающиеся на ab, aB, Ab, AB.

Пример

 RegexOptions regexOptions = new RegexOptions(); regexOptions.setRegexCaseInsenssitive(); Query query = new Query(“mycollection”).startsWith ("exampleField", "a", regexOptions) //query.findDocuments(…);

.and(field, query)

Метод логического умножения нескольких условий выборки по одному полю

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
query Query Обязательный Объект класса Query, содержащий данные для выборки см.пример ниже

Пример

Query query1 = new Query(COLLECTION_NAME).greaterThan("raiting", 50); Query query2 = new Query(COLLECTION_NAME).lessThan("raiting", 100); query1.and("number3", query2); //query1.findDocuments(…);

.or(field, query)

Метод логического сложения нескольких условий выборки по одному полю

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»
query Query Обязательный Объект класса Query, содержащий данные для выборки см.пример ниже

Пример

Query query1 = new Query(“mycollection”).greaterThan("raiting", 50); Query query2 = new Query(“mycollection”).equalTo("status", 0); query1.or("number3", query2); //query1.findDocuments(…);

.raw(json)

Метод для задания условий выборки в формате json.

Параметр Тип Свойства Описание Пример значения
json String Обязательный Условия выборки в формате json «>»

Пример

 Query query = new Query(“mycollection”); query.raw(">"); //query.findDocuments(…)

.reset()

Метод для очистки условия выборки.

Пример

Query query = new Query(“mycollection”); query.equalTo(“_id”, “dsads123sd”); query.reset(); query.equalTo(“_id”, “ds54522sd”); //query.findDocuments(…)

.ascending(field)

Метод для сортировки данных указанного поля в порядке возрастания перед совершением выборки.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»

Пример

Query query = new Query("ordersCollection"); query.ascending("itemId"); //query.findDocuments(. )

.descending(field)

Метод для сортировки данных указанного поля в порядке убывания перед совершением выборки.

Параметр Тип Свойства Описание Пример значения
field String Обязательный Имя поля, которому задается условие «fieldname»

Пример

Query query = new Query("ordersCollection"); query.descending("itemId"); //query.findDocuments(. )

.setFieldsForSearch(fields)

Метод для задания списка возвращаемых полей.

Параметр Тип Свойства Описание Пример значения
fields List

Обязательный Имена полей см.пример ниже

Пример

List fieldNames = new ArrayList<>(); fieldNames.add(“orderId”); fieldNames.add(“buyerName”); fieldNames.add(“phoneNumber”); Query query = new Query(“mycollection”).setFieldsForSearch(fieldNames); //query.findDocuments(…);

.getQueryInfo()

Метод для получения информации о условиях выборки

Пример

Query query = new Query(“mycollection”); query.equalTo(“_id”, “dsads123sd”); QueryInfo queryInfo = query.getQueryInfo();

Аннотация @Query

Мы рассматривали запросы, генерируемые из имени метода. Так удобно писать простые запросы. Но если в запросе более 1-2 параметров или несколько join, то удобнее использовать аннотацию @Query.
Для этого в репозитории создаем метод, аннотированный @Query, и внутри аннотации прописываем запрос — можно как JPQL, так и native SQL.

Ниже приведен пример.

Использование @Query

public interface PostRepository extends JpaRepository  < @Transactional(readOnly = true) @Query("select p from Post p where p.user.id=:id and p.title like :title") ListfindMySuperPosts(String title, long id); >

Запрос выбирает посты данного пользователя по заголовку.

Посты и пользователи находятся в отношении @ManyToOne. Класс Post:

@Entity @Data @NoArgsConstructor public class Post
@Data @Entity @NoArgsConstructor public class User

Native Query

Такой же запрос можно написать на SQL, указав nativeQuery = true:

@Transactional(readOnly = true) @Query(nativeQuery = true, value = "select * from post p where p.user_id=:id and p.title like :title") List findMySuperPostsNative(String title, long id);

Pagination и Sorting

Можно получать результат постранично, указав в качестве параметра Pagable:

@Transactional(readOnly = true) @Query("select p from Post p where p.title like :title") List findSuperPosts(String title, Pageable pageable);

Создавать запрос страницы можно так:

Pageable pageable= PageRequest.of(0, 2, Sort.by("title"));

Выше приведен запрос первой страницы (страницы нумеруются с 0), при этом на странице должно быть два элемента. Задано упорядочивание выборки по title. Подробнее о постраничном выводе и сортировке тут.

Формируемый SQL такой:

select post0_.id as id1_0_, post0_.text as text2_0_, post0_.title as title3_0_, post0_.user_id as user_id4_0_ from post post0_ where post0_.title like ? order by post0_.title asc limit ? select user0_.id as id1_1_0_, user0_.email as email2_1_0_, user0_.locked as locked3_1_0_, user0_.nickname as nickname4_1_0_, user0_.password as password5_1_0_, user0_.role as role6_1_0_ from user user0_ where user0_.id=?

Аннотация @Modifying

С помощью @Query можно не только читать, но и обновлять базу. Но в таких запросах необходима дополнительная к @Query аннотация @Modifying.

update

Пример обновления заголовков всех постов пользователя:

@Modifying @Transactional @Query("update Post p set p.title=:title where p.id=:id") int setFixedPostTitleFor(String title, long id);
delete

Пример удаления всех постов пользователя:

@Modifying @Transactional @Query("delete from Post p where p.user.id=:id") int deleteInBulkByUserId(long id);

Стоит отметить, что так посты удаляются пакетно, поскольку генерируется один SQL :

delete from post where user_id=?

Если же использовать генерируемый по имени метод, то посты удаляются по одному:

@Transactional int deleteByUserId(long id);

SQL отправляется свой для каждого поста:

Hibernate: delete from post where delete from post where delete from post where >Обратите внимание, что методы мы аннотировали @Transactional, поскольку писали их сами. (Если бы использовали стандартные методы репозитория, унаследованные от SimpleJpaRepository, то они уже @Transactional.)

Все методы должны быть в рамках транзакции, другое дело что @Transactional может быть на уровне сервиса или теста. Но в данном случае тест у нас не @Transactional, класс аннотирован @SpringBootTest, а не @DataJpaTest (внутри которого есть @Transactional). Это потому, что @DataJpaTest откатывает транзакции и некоторые SQL не логируются (возможно, потому что не выполняются?). А хотелось бы видеть все сгенерированные SQL.

@SpringBootTest public class PostRepositoryTest < @Autowired private PostRepository postRepository; @DirtiesContext @DisplayName("посты должны удаляться по user.id единым запросом в @Query") @Test void titlesShouldBeDeletedByIdInBulk() < int n = postRepository.deleteInBulkByUserId(1l); Assertions.assertEquals(3, n); >. >

А для того, чтобы подтвержденные транзакции не портили базу после каждого теста, каждый тест аннотирован @DirtiesContext — это пересоздает H2 базу. Не оптимально, зато наглядная консоль с выводом всех SQL-запросов.

Итоги

Исходный код есть на GitHub.

Изучаем класс Query

Кстати, еще один важный момент – это вспомогательный класс Query. Ты мог его видеть вот в этом примере:

 public List getAllEmployes() < try (Session session = sessionFactory.openSession()) < Queryquery = session.createQuery("from Employee", Employee.class); return query.list(); > > 

На самом деле, Query – это интерфейс и у него есть несколько реализаций на разные случаи. Но для простоты я буду продолжать называть его классом. Это, скажем так, класс в широком смысле – в терминах ООП.

Примечание. Раньше было два класса:

  • Query для описания запроса.
  • TypedQuery для описания запроса с заранее известным типом.

Первый появился, когда Hibernate уже был, а дженериков еще не было. Потом, после выхода JDK 5, в Hibernate добавили еще один класс – TypedQuery, который уже поддерживал типизацию результата запроса.

Но, насколько я помню, начиная с 5-й версии Hibernate оставили только один типизированный класс, и он теперь называется Query.

Стандартный способ создания Query:

 Query query = session.createQuery("from Employee", Employee.class); 

Объекты Query ты создавать научился, а как эти запросы выполнить?

Тут все еще проще – мы просто вызываем метод list() у объекта Query:

 Query query = session.createQuery("from Employee", Employee.class); List resultLіst = query.list(); 

У метода list() есть JPA-синоним, метод который делает тоже самое, но называется getResultList(). Ты можешь иногда встретить его в коде, написанном другими программистами.

Кстати, если запрос подразумевает, что результат будет в единственном результате, то для вызова запроса проще использовать метод uniqueResult().

 Query query = session.createQuery("from Employee where Employee.class); Employee result = query.uniqueResult(); 

У метода uniqueResult() есть JPA-синоним – метод singleResult(). Он появился для совместимости Hibernate со стандартом JPA. Делает он абсолютно то же самое.

2.2 Методы класса Query

На самом деле, у класса Query есть очень много различных методов. Ниже я расскажу еще о трех из них.

Во-первых, это метод stream(). И его JPA-синоним getResultStream().

Оба этих метода возвращают поток данных вместо списка. Такой подход может быть очень эффективным, когда тебе не нужны сразу все объекты, полученные в результате выполнения запроса. Или есть вероятность, что будут использованы только первые из них.

 Query query = session.createQuery("from Employee where id > 100", Employee.class); Stream stream = query.stream(); 

Второй метод – это метод executeUpdate(). Ты можешь написать запрос, который что-то изменит в базе данных. На этот случай нужно, чтобы Hibernate не использовал read-only транзакцию при обращении к базе данных.

Пример запроса: мы решили поднять уровень всех пользователей на 1.

 Query query = session.createQuery("update User set level=level+1", User.class); int count = query.executeUpdate(); 

Метод executeUpdate() вернет количество строк, которые реально были изменены.

И наконец третий метод – это scroll(). О нем мы расскажем немного подробнее.

2.3 Методы класса Scroll

Этот метод чем-то похож на метод stream(). Только он позволяет перемещаться по списку результатов, не вытаскивая результаты вообще. То есть ты можешь выполнить запрос, потом проскролить его на миллионную строку результата и начать читать оттуда данные.

Такой продвинутый итератор.

 Query query = session.createQuery("from Employee where id > 100", Employee.class); ScrollableResults scroll = query.scroll(); 

У объекта ScrollableResults есть такие методы:

Метод Описание
R get() Возвращает текущий элемент
next() Перемещает указатель на следующий элемент
previous() Перемещает указатель на предыдущий элемент
scroll(int size) Скролит на size строк вперед
position(int pos) Делает текущим элементом элемент номер pos
last() Текущий элемент теперь последний
first() Текущий элемент теперь первый
getRowNumber() Возвращает номер текущей строки
setRowNumber() Устанавливает номер текущей строки

Допустим, ты выполнил запрос и хочешь получить последний элемент. Вот как это можно сделать:

 Query query = session.createQuery("from Employee where id > 100", Employee.class); ScrollableResults scroll = query.scroll(); scroll.last(); Employee lastEmployee = scroll.get(); 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *