Обновившись до Spring Boot 3.3.0 (конкретно до Hibernate 6.5), мы столкнулись со 100% загрузкой процессора на БД из-за небольшого изменения в SQL коде, сгенерированного Hibernate после преобразования JPQL в SQL.
Посмотрите на следующий JPQL-запрос и обратите внимание на различия в генерации SQL для Hibernate 6.4 и Hibernate 6.5 при передаче пустого списка в качестве параметра.
interface ArticleRepository extends CrudRepository<Article, UUID> {
@Query("from Article where publisherId in :ids")
List<Article> findByPublisherId(List<UUID> ids);
}
var articles = articleRepository.findByPublisherId(List.of());
Hibernate 6.4.X:
select a1_0.id, a1_0.publisher_id, a1_0.title
from article a1_0
where 1=0
Hibernate 6.5.X:
select a1_0.id, a1_0.publisher_id, a1_0.title
from article a1_0
where (1 = case when a1_0.publisher_id is not null then 0 end)
Может показаться, что разница незначительная, однако в PostgreSQL первый запрос выполняется практически мгновенно, а второй приводит к полному сканированию таблицы (full table scan).
Стоит отметить, что при использовании derived-query вместо @Query с JPQL запрос будет отличаться:
interface ArticleRepository extends CrudRepository<Article, UUID> {
List<Article> findByPublisherId(List<UUID> ids);
}
var articles = articleRepository.findByPublisherId(List.of());
select a1_0.id, a1_0.publisher_id, a1_0.title
from article a1_0
where a1_0.publisher_id in (?)
Команда Hibernate уже поправила этот баг: https://github.com/hibernate/hibernate-orm/pull/8528
Осталось дождаться включения этого фикса в модуль Spring Data JPA.
Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм - Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
Ждем всех, присоединяйтесь!
Комментарии (11)
aleksandy
07.06.2024 08:44+2Осталось дождаться включения этого фикса в модуль Spring Data JPA.
А зачем дожидаться? Если можно просто обновить используемую версию хибера? Или автор свидетель тождественности спрингдатажпа и с хибером?
spring_aio Автор
07.06.2024 08:44+2На самом деле, надо дождаться, пока выйдет новая версия Hibernate. Баг поправили в мастере.
aleksandy
07.06.2024 08:44Ну, собственно, о том и речь. Багу исправили в хибере и к спринг дате он относится чуть менее, чем никак.
Slobodator
07.06.2024 08:44+5С
in-clause
вообще лучше быть осторожным. В постгресе вроде бы ограничений нет, а в оракле, например, по дефолту не более 1000 аргументов -- соответственно, надо разбивать на чанки и конкатенировать результат.Если бизнес-логика позволяет (как в данном конкретном случае), имеет смысл предварить в репозитории
interface ArticleRepository extends CrudRepository<Article, UUID> { default List<Article> findByPublisherId(List<UUID> ids) { if (ids.isEmpty()) { return Collections.emtpyList(); } return _findByPublisherId(ids); } @Query("from Article where publisherId in :ids") List<Article> _findByPublisherId(List<UUID> ids); }
Если метод совсем безобразно могут вызывать, ещё и проверку на
null
добавить.
DieSlogan
07.06.2024 08:44+2Не была печали, апдейтов накачали.
Сори, но зачем вам edge обновления, когда можно спокойно сидеть на стабильной ветке, просто со всеми патчами безопасности?
Sipaha
А зачем вообще hibernate отправляет заведомо бесполезный запрос в бд в этом случае?
spring_aio Автор
Конкретно здесь, может быть и можно было обойтись без запроса вообще. Наверно это не так просто сделать, когда это подзапрос, или какой-то более сложный кейс, с джоинами например.
Возможно, чтобы не городить множество оптимизаций на все случаи жизни, просто отключают ветку с помощью условия 1 = 0;
qwzx
Интересно другое: перед каждым запросом к базе с новыми параметрами генерируется новый SQL в зависимости от параметров?
ris58h
Этот вопрос надо не Hibernate адресовать, а Spring-у.
Suvitruf
Почему?
ris58h
Вы правы - не туда посмотрел - думал что речь про derived-query.