Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье мы рассмотрим, как мы можем загружать несколько объектов по идентификатору одновременно при использовании JPA и Hibernate.
Загрузка нескольких объектов по их идентификатору является очень распространенным требованием при использовании JPA и Hibernate. Следовательно, мы увидим, как мы можем оптимизировать выполнение базового SQL-запроса.
Модель предметной области
Для следующих примеров мы будем использовать следующую Книгу
сущность:
Теперь давайте рассмотрим, что мы добавили несколько Книг
сущностей в нашу базу данных:
entityManager.persist( new Book() .setIsbn("978-9730228236") .setTitle("High-Performance Java Persistence") .setAuthor("Vlad Mihalcea") ); entityManager.persist( new Book() .setIsbn("978-1934356555") .setTitle("SQL Antipatterns") .setAuthor("Bill Karwin") ); entityManager.persist( new Book() .setIsbn("978-3950307825") .setTitle("SQL Performance Explained") .setAuthor("Markus Winand") ); entityManager.persist( new Book() .setIsbn("978-1449373320") .setTitle("Designing Data-Intensive Applications") .setAuthor("Martin Kleppmann") );
Обратите внимание, как хорошо мы можем создавать экземпляры сущностей JPA, когда атрибуты соответствуют рекомендациям API в стиле fluent.
Для получения более подробной информации об использовании шаблона проектирования API в стиле fluent ознакомьтесь с этой статьей .
Извлечение нескольких объектов по идентификатору с помощью JPA
Предположим, что пользователь предоставляет несколько номеров книг ISBN, и приложению необходимо загрузить связанные Книги
сущности из базы данных.
При использовании JPA мы можем загружать несколько объектов одновременно, используя JPQL или API критериев.
Извлечение нескольких объектов по идентификатору с помощью JPQL
Чтобы извлечь несколько сущностей с помощью запроса JPQL, нам нужно передать идентификаторы сущностей в предложение IN, как показано в следующем примере:
Listbooks = entityManager.createQuery(""" select b from Book b where b.isbn in (:isbn) """, Book.class) .setParameter( "isbn", Arrays.asList( "978-9730228236", "978-1934356555", "978-3950307825" ) ) .getResultList();
Теперь при выполнении приведенного выше запроса JPQL Hibernate генерирует следующий SQL-запрос:
Query:[" SELECT b.isbn AS isbn1_0_, b.author AS author2_0_, b.title AS title3_0_ FROM book b WHERE b.isbn IN ( ?, ?, ? ) "], Params:[( 978-9730228236, 978-1934356555, 978-3950307825 )]
Лучший способ регистрации инструкций SQL-это использовать JDBC источник данных
или Драйвер
прокси-сервер в качестве проекта с открытым исходным кодом datasourceproxy
.
Для получения более подробной информации об этой теме ознакомьтесь с этой статьей .
Теперь, если вы измените количество параметров предложения IN, вы увидите, что инструкция SQL должна соответствовать количеству параметров привязки, и это может повлиять на эффективность механизма кэширования инструкций SQL. Чтобы справиться с этой проблемой, я добавил поддержку hibernate.запрос.in_clause_parameter_padding
свойство конфигурации.
Итак, при включении спящий режим.запрос.in_clause_parameter_padding
свойство конфигурации:
name="hibernate.query.in_clause_parameter_padding" value="true"
Hibernate будет использовать ближайшее целое число степени два, которое может содержать все предоставленные значения параметров привязки. Поэтому при повторном запуске предыдущего запроса JPQL Hibernate выполнит следующую инструкцию SQL:
Query:[" SELECT b.isbn AS isbn1_0_, b.author AS author2_0_, b.title AS title3_0_ FROM book b WHERE b.isbn IN ( ?, ?, ?, ? ) "], Params:[( 978-9730228236, 978-1934356555, 978-3950307825, 978-3950307825 )]
Обратите внимание, как 4 – й параметр был продублирован в наборе значений параметра привязки. Это позволит нам повторно использовать один и тот же план выполнения инструкции SQL, даже если мы приведем четыре аргумента, как показано в следующем запросе JPQL:
Listbooks = entityManager.createQuery( select b from Book b where b.isbn in (:isbn) """, Book.class) .setParameter( "isbn", Arrays.asList( "978-9730228236", "978-1934356555", "978-3950307825", "978-1449373320" ) ) .getResultList();
Спящий режим.запрос.in_clause_parameter_padding
оптимизация особенно полезна при использовании системы баз данных, предоставляющей кэш плана выполнения SQL, например Oracle, SQL Server или DB2.
Для получения более подробной информации о hibernate.запрос.in_clause_parameter_padding
параметр конфигурации, проверьте эту статью .
Извлечение нескольких объектов по идентификатору с помощью API критериев
Предыдущий запрос JPQL также может быть написан с использованием API критериев JPA, как показано в следующем примере:
CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQueryquery = builder.createQuery(Book.class); ParameterExpression isbn = builder.parameter(List.class); Root
root = query .from(Book.class); query .where( root.get("isbn").in( isbn ) ); List books = entityManager .createQuery(query) .setParameter( isbn, Arrays.asList( "978-9730228236", "978-1934356555", "978-3950307825" ) ) .getResultList();
Как и в случае с JPQL-запросами, вы можете использовать hibernate.query.in_clause_parameter_padding
оптимизация для повышения эффективности механизма кэширования инструкций SQL.
Извлечение нескольких объектов по идентификатору с помощью Hibernate
Помимо поддержки всех ранее упомянутых функций JPA, Hibernate предоставляет специфичный для поставщика способ загрузки нескольких объектов по их идентификатору.
Listbooks = entityManager .unwrap(Session.class) .byMultipleIds(Book.class) .multiLoad( "978-9730228236", "978-1934356555", "978-3950307825" );
При выполнении приведенного выше примера MultiLoad
Hibernate создаст следующий SQL-запрос:
Query:[" SELECT b.isbn AS isbn1_0_, b.author AS author2_0_, b.title AS title3_0_ FROM book b WHERE b.isbn IN ( ?, ?, ? ) "], Params:[( 978-9730228236, 978-1934356555, 978-3950307825 )]
Хотя метод MultiLoad
намного более лаконичен, чем запрос JPQL или API критериев, в настоящее время он не использует hibernate.запрос.in_clause_parameter_padding
оптимизация. Тем не менее, я открыл для этого проблему HHH-13692 Jira, и это должно быть исправлено в будущей версии Hibernate.
Еще одним преимуществом использования метода MultiLoad
является то, что сущности могут быть загружены из кэша первого или второго уровня без необходимости выполнения SQL-запроса.
Вывод
Извлечение нескольких объектов по их идентификатору является типичным требованием при использовании JPA и Hibernate. Хотя использование запроса JPQL или API критериев может показаться немного подробным по сравнению с альтернативой Hibernate MultiLoad
, если вы используете систему баз данных, предлагающую кэш плана выполнения SQL, то лучше придерживаться подхода JPA-запросов при включении hibernate.запрос.in_clause_parameter_padding
оптимизация.