HIBERNATE Flashcards

1
Q

Что такое ORM?

A

ORM == Object-Relational Mapping (объектно-реляционное отображение, преобразование).

ORM (Object-Relational Mapping) - это технология, которая позволяет связывать объектно-ориентированную модель данных и реляционную базу данных и наоборот, т.е. позволяет работать с таблицами БД как с объектами.
(Преобразовывает объект в строку в таблице и наоборот)

В Java это делается с помощью рефлексии и JDBC.

Создает дополнительный слой внутри приложения, который будет отвечать за связь приложения с БД и преобразование данных БД в Объекты и наоборот.

КОД <---> ORM ОБЪЕКТЫ <---> БД(ТАБЛИЦЫ

ORM-фреймворки обычно позволяют создавать классы, которые соответствуют таблицам в базе данных, а также мапперы для связи между классами и таблицами. Они также предоставляют методы для выполнения CRUD-операций (Create, Read, Update, Delete) над данными.

ORM-фреймворки упрощают процесс разработки, поскольку разработчики могут использовать язык JAVA для работы с данными вместо написания SQL-запросов и последующими манипуляциями с результатами запросов.

ORM позволяет работать с объектами в памяти, что улучшает производительность приложения и позволяет избежать частых обращений к БД.

Они также позволяют скрыть детали взаимодействия с базой данных, такие как создание подключения, открытие и закрытие транзакций и т.д., что делает код более читабельным и поддерживаемым, а так же снижается вероятность ошибок, связанных с написанием SQL-запросов вручную.

Некоторые из наиболее популярных ORM-фреймворков для Java включают Hibernate, EclipseLink, MyBatis, JOOQ, Spring Data JPA и т.д.

1) ORM - технология программирования, которая связывает базы данных с объектами JAVA.
2) Тем самым получается “объектная база данных”.
3) В этом случае программист избавлен от необходимости писать конкретные sql-запросы и вручную конструировать объекты из полей таблицы БД.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Что такое JPA?

A

Java Persistence API - это спецификация (JSR 338 стандарт, технология) используемая для управления отображаемыми(представляемыми) POJO объектами и их хранением в реляционной базе данных.

Это спецификация Java, которая предоставляет набор интерфейсов/аннотаций для возможности сохранять в удобном виде Java объекты в БД и наоборот - извлекать информацию из БД в виде Java объектов.

Он предоставляет унифицированный интерфейс(Конкретное описание работы ORM принципов) для работы с ORM (Object-Relational Mapping) в Java-приложениях.
Он также предоставляет API для управления жизненным циклом объектов и выполнения операций CRUD (создание, чтение, обновление и удаление) в базе данных.

JPA позволяет разработчикам Java использовать объектно-ориентированный подход к работе с базами данных, что в свою очередь:
* упрощает разработку приложений
* позволяет более эффективно использовать базы данных
* снижает количество кода для работы с ними;

Сам JPA не умеет работать с объектами(не умеет ни сохранять, ни управлять объектами), JPA только определяет правила игры: как должен действовать каждый провайдер (Hibernate, EclipseLink, OJB, Torque и т.д.), реализующий стандарт JPA. Для этого JPA определяет интерфейсы, которые должны быть реализованы провайдерами. Каждый провайдер обязан реализовывать все из JPA, определяя стандартное получение, сохранение, управление объектами. Помимо этого, провайдеры могут добавлять свои личные классы и интерфейсы, расширяя функционал JPA.

Также JPA определяет правила, как должны описываться метаданные отображения и как должны работать провайдеры.
JPA определяет аннотации, которые могут использоваться для указания отображаемых объектов и связей между ними.
JPA является частью стандартной спецификации Java EE (Enterprise Edition) и может использоваться в любом Java-приложении, независимо от того, используется ли контейнер приложений Java EE или нет.

Гибкость JPA.
JAVA-код, написанный только с использованием интерфейсов и классов JPA, позволяет разработчику гибко менять одного провайдера на другого. Например, если приложение использует Hibernate как провайдера, то ничего не меняя в коде можно поменять провайдера на любой другой. Но, если мы в коде использовали интерфейсы, классы или аннотации, например, из Hibernate, то поменяв провайдера на EclipseLink, эти интерфейсы, классы или аннотации уже работать не будут.

1) JPA - это спецификация API Java Enterprise. Интерфейс предоставляет возможность сохранять в удобном виде Java-объекты в базе данных.
2) JPA реализует концепцию ORM - object-relational mapping.
3) Одна из самых популярных реализаций JPA - библиотека Hibernate.

JPA:
JPA поддерживает использование JPQL (Java Persistence Query Language) для выполнения запросов к базе данных, что упрощает написание и поддержку запросов в приложении.

JPA предоставляет возможность использования кэширования объектов для улучшения производительности приложения.
JPA позволяет использовать различные стратегии отображения объектов на таблицы базы данных, что позволяет разработчику выбрать оптимальный подход в зависимости от конкретных требований приложения.

JPA поддерживает использование аннотаций для управления отображением Java-объектов на таблицы базы данных, что делает код более читаемым и упрощает разработку приложений.

JPA позволяет использовать различные типы отношений между объектами, такие как один-к-одному, один-ко-многим и многие-ко-многим, что позволяет разработчику создавать более сложные приложения с эффективным использованием базы данных.

JPA также предоставляет возможность использования транзакций для обеспечения целостности данных при выполнении операций с базой данных.

JPQL -Java Persistence Query Language - это объектный язык запросов (запросы выполняются к объектам), используемый в Java Persistence API (JPA) для работы с базами данных.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Что такое JDBC?

A

JDBC (Java DataBase Connectivity)
1) Это набор API в JDK для доступа к реляционным базам данных из программы Java.
2) Эти API позволяют из Java-кода взаимодействовать с (почти) любой SQL базой данных.
3) Например, подключиться, создать таблицы, выполнять SQL-запросы и т.д.

JDBC - Java DataBase Connectivity — API для работы с реляционными (зависимыми) БД. Платформенно независимый промышленный стандарт взаимодействия Java-приложений с различными СУБД, реализованный в виде пакета java.sql, входящего в состав Java SE. Предоставляет методы для получения и обновления данных. Не зависит от конкретного типа базы. Библиотека, которая входит в стандартную библиотеу, содержит: набор классов и интерфейсов для работы с БД (для нас разработчиков api) + интерфейсы баз данных.

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

Наш код –> JDBC –> драйвера разработчиков БД –> БД.

JDBC has 3 entities:

  • Connection (класс). Объект которого отвечает за соединение с базой и режим работы с ней (лекция 3.1.4)
    Statement (объект для оператора JDBC) используется для отправки SQL-оператора на сервер баз данных. Объект для оператора связан с объектом Connection и является объектом, обрабатывающим взаимодействие между приложением и сервером баз данных.
    Можно:
    что-то поменять Update statement (create, delete, insert) в базе;
    что-то запросить Query statement (select) из базы;
    Для разных задач есть разные виды Statement-ов:
  • statement - обычный. передаем в него либо Update либо Query
    PreparedStatement - возможность сделать некий шаблон запроса, подставлять в него кто-то значения и использовать его
    CallableStatement - предоставляет возможность вызова хранимой процедуры, расположенной на сервере, из Java-приложения.
  • ResultSet. Объект с результатом запроса, который вернула база. Внутри него таблица.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Различия JPA JDBC и Hibernate?

A

JDBC является гораздо более низкоуровневой (и более старой) спецификацией, чем JPA. JDBC - это API-интерфейс для взаимодействия с базой данных с использованием чистого SQL - отправки запросов и получения результатов. Он не имеет понятия об объектах или иерархиях. При использовании JDBC вам необходимо преобразовать набор результатов в объекты Java.

А в JPA (который использует JDBC под капотом) вы также указываете эти детали метаданных базы данных, но с использованием аннотаций Java. Таким образом, JPA создает запросы на обновление для вас и управляет объектами, которые вы искали или создали / обновили (это также делает больше).

Hibernate одна из самых популярных открытых реализаций последней версии спецификации. То есть JPA только описывает правила и API, а Hibernate реализует эти описания.

  • Hibernate удаляет множество повторяющегося кода из JDBC API, а следовательно его легче читать, писать и поддерживать.
  • Hibernate поддерживает наследование, ассоциации и коллекции, что не доступно в JDBC API.
  • Hibernate неявно использует управление транзакциями. Большинство запросов нельзя выполнить вне транзакции. При использовании JDBC API для управления транзакциями нужно явно использовать commit и rollback.
  • JDBC API throws SQLException, которое относится к проверяемым исключениям, а значит необходимо постоянно писать множество блоков try-catch. В большинстве случаев это не нужно для каждого вызова JDBC и используется для управления транзакциями. Hibernate оборачивает исключения JDBC через непроверяемые JDBCException или HibernateException, а значит нет необходимости проверять их в коде каждый раз. Встроенная поддержка управления транзакциями в Hibernate убирает блоки try-catch.
  • Hibernate Query Language (HQL) более объектно ориентированный и близкий к Java язык запросов, чем SQL в JDBC.
  • Hibernate поддерживает кэширование, а запросы JDBC — нет, что может понизить производительность.
  • Hibernate поддерживает аннотации JPA, а значит код является переносимым на другие ORM фреймворки, реализующие стандарт, в то время как код JDBC сильно привязан к приложению.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Что такое Hibernate?

A

Hibernate - это фреймворк на базе Java с открытым исходным кодом, реализующий спецификацию JPA. Hibernate полностью реализует JPA плюс добавляет функционал в виде своих классов и интерфейсов, расширяя свои возможности по работе с сущностями и БД.

Hibernate предоставляет разработчикам удобный способ работы с базами данных, позволяя им работать с базами данных через объектно-ориентированный интерфейс.

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

Краткий принцип работы:
JAVA APPLICATION(SAVE) ---> HIBERNATE
* Сбор данных из полей объекта
* Написание INSERT выражения для добавления новой строки в таблицу с собранными данными

JAVA APPLICATION(GET) <--- HIBERNATE
* Написание SELECT выражения для получения необходимых данных
* Создание объекта Java Класса и присвоение его полям значений, полученных из базы

Hibernate использует JDBC для работы с БД.

Основные возможности фреймворка:

1) Hibernate связывает классы Java с таблицами данных SQL.
2) Автоматическая генерация и обновление таблиц в базах данных;
3) Поскольку основные запросы к базе данных (сохранение, обновление, удаление и поиск) представлены как методы фрейморка, то значительно сокращается код, который пишется разработчиком ;
4) Обеспечивает использование SQL подобного языка (HQL — hibernate query language). Запросы HQL могут быть записаны рядом объектами данных (POJO классы подготовленные для работы с базой данных).
5) Автоматизирует генерацию SQL-запросов на “диалекте” текущей базы данных.
6) Hibernate поддерживает различные базы данных

Важные интерфейсы Hibernate:
Session extends EntityManager – обеспечивает физическое соединение между приложением и БД. Это единица работы Hibernate осуществляющая DML команды.(не является потокобезопасным объектом.)
SessionFactory – объект, который создает во время запуска приложения сессии работы с БД и сохраняет для последующего использования. Обычно создается одна sessionFactory на одну базу данных. Внутри сессий проходят транзакции. Является потокобезопасным объектом и используется всеми потоками приложения.
Transaction – представляет собой атомарную единицу работы с базой данных. Это абстракция приложения от основных JDBC-транзакций. Сессия может включать несколько транзакций.
Query – интерфейс позволяет выполнять запросы к БД. Может использовать объектный (HQL) или обычный (SQL) язык запросов.
Configuration - представляет собой объект с параметрами конфигурации, требуемыми Hibernate.
Criteria - Используется для создания и выполнения объекто-ориентированных запроса на получение объектов. Является альтернативой использованию Query. Criteria − объект, позволяющий получать из базы данных объекты по определенным критериям.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

В чем заключаются преимущества использования Hibernate?

A
  • Hibernate устраняет множество спагетти кода (повторяющегося), который постоянно преследует разработчика при работе с JDBC. Скрывает от разработчика множество кода, необходимого для управления ресурсами и позволяет сосредоточиться на бизнес логике.
  • Hibernate поддерживает XML так же как и JPA аннотации, что позволяет сделать реализацию кода независимой.
  • Hibernate предоставляет собственный мощный язык запросов (HQL), который похож на SQL. Стоит отметить, что HQL полностью объектно-ориентирован и понимает такие принципы, как наследование, полиморфизм и ассоциации (связи).
  • Hibernate легко интегрируется с другими Java EE фреймворками, например, Spring Framework поддерживает встроенную интеграцию с Hibernate.
  • Hibernate поддерживает ленивую инициализацию используя proxy объекты и выполняет запросы к базе данных только по необходимости.
  • Hibernate поддерживает разные уровни cache, а следовательно может повысить производительность.
  • Важно, что Hibernate может использовать чистый SQL, а значит поддерживает возможность оптимизации запросов и работы с любым сторонним вендором БД и его фичами.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Конфигурация Hibernate

A

Минимальная конфигурация Hibernate включает в себя несколько основных компонентов, которые необходимы для установки соединения с базой данных и запуска ORM (Object-Relational Mapping) слоя:

  • Конфигурационный файл hibernate.cfg.xml: это файл конфигурации Hibernate, который содержит информацию о настройках подключения к базе данных, таких как JDBC URL, логин, пароль, диалект SQL и т.д.
    Пример hibernate.cfg.xml:
    ~~~
    <?xml version=”1.0” encoding=”UTF-8”?>
    <!DOCTYPE hibernate-configuration PUBLIC
    “-//Hibernate/Hibernate Configuration DTD 3.0//EN”
    “http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd”>

<hibernate-configuration>
<session-factory>
<property>org.hibernate.dialect.MySQL5Dialect</property>
<property>com.mysql.jdbc.Driver</property>
<property>jdbc:mysql://localhost:3306/mydb</property>
<property>root</property>
<property>password</property>
<mapping></mapping>
<mapping></mapping>
</session-factory>
</hibernate-configuration>


Configuration configuration = new Configuration();
configuration.configure(“hibernate.cfg.xml”);
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
~~~

  • в Hibernate можно сконфигурировать соединение с базой данных и другие параметры без использования файла hibernate.cfg.xml. В этом случае конфигурация будет задана программно.
    Например, для создания сессионной фабрики без файла hibernate.cfg.xml можно использовать следующий код:
    ~~~
    Configuration configuration = new Configuration()
    .setProperty(“hibernate.dialect”, “org.hibernate.dialect.MySQL5Dialect”)
    .setProperty(“hibernate.connection.driver_class”, “com.mysql.jdbc.Driver”)
    .setProperty(“hibernate.connection.url”, “jdbc:mysql://localhost:3306/mydb”)
    .setProperty(“hibernate.connection.username”, “root”)
    .setProperty(“hibernate.connection.password”, “password”);
    .setProperty(“hibernate.show_sql”, “true”)
    .setProperty(“hibernate.hbm2ddl.auto”, “update”)
    .addAnnotatedClass(User.class)
    .addAnnotatedClass(Order.class);
    SessionFactory sessionFactory = configuration.buildSessionFactory();
    ~~~
    В этом примере мы создаем объект Configuration и устанавливаем необходимые параметры конфигурации с помощью метода setProperty(). Затем мы добавляем аннотированные классы User и Order с помощью метода addAnnotatedClass(). Наконец, мы создаем сессионную фабрику с помощью метода buildSessionFactory().
    Преимуществом программной конфигурации является возможность управления настройками Hibernate в коде и настройка их в зависимости от условий. Также это может быть полезно для тестирования и экспериментирования с различными конфигурациями без необходимости изменения файла hibernate.cfg.xml. Однако, этот способ конфигурации может быть менее удобным для конфигурации крупных проектов и может потребовать больше усилий для настройки всех параметров.
    Далее:
    ~~~
    Session session = sessionFactory.openSession();
    Transaction transaction = null;
    try {
    transaction = session.beginTransaction();
    // выполнение операций с объектами
    transaction.commit();
    } catch (Exception e) {
    if (transaction != null) {
    transaction.rollback();
    }
    e.printStackTrace();
    } finally {
    session.close();
    }
    ~~~
  • Так же возможна конфигурация с помощью:
    Интеграция с Spring: если проект использует Spring Framework, то для конфигурации Hibernate можно использовать специальные библиотеки, такие как Spring ORM или Spring Data.
  • Использование JPA: Hibernate является одной из реализаций стандарта JPA (Java Persistence API). Поэтому для конфигурации Hibernate можно использовать стандартные средства JPA, такие как файл persistence.xml.
  • Использование аннотаций: Hibernate позволяет использовать аннотации для задания маппинга между классами и таблицами базы данных. В этом случае конфигурация задается непосредственно в коде с помощью аннотаций.
    Для того чтобы сконфигурировать возможность подключения к базе данных с помощью аннотаций, можно использовать аннотацию @PersistenceContext вместе с аннотацией @Transactional для указания, что метод должен выполняться в контексте транзакции.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Что такое Entity?

A

Entity в Hibernate - это класс Java, который представляет собой объект, который может быть сохранен в базе данных.
POJO класс становится Entity классом с помощью аннотации @Entity.
Entity отображает таблицу в базе данных и содержит поля, которые соответствуют столбцам этой таблицы.
Каждый объект Entity имеет уникальный идентификатор, который используется для идентификации объекта в базе данных.(ID) Он должен реализовывать интерфейс Serializable
Entity также может иметь отношения с другими Entity, что позволяет описать связи между таблицами в базе данных.

В Hibernate Entity обычно является классом Java, который содержит поля, геттеры и сеттеры для этих полей, конструкторы и методы, которые определяют поведение объекта. Класс Entity также может иметь аннотации, которые определяют маппинг между полями класса и столбцами таблицы в базе данных.

Кроме того, Entity может иметь отношения с другими Entity, что позволяет описать связи между таблицами в базе данных. Например, одна Entity может иметь отношение “один-ко-многим” с другой Entity, что означает, что один объект этой Entity может иметь несколько связанных объектов другой Entity.

Эти поля или свойства используют аннотации объектно-реляционного сопоставления (маппинга) для сопоставления сущностей и отношений между ними с реляционными данными в хранилище данных. Примеры аннотаций: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany.

Hibernate использует Entity для выполнения операций CRUD (создание, чтение, обновление и удаление) в базе данных.

В целом, Entity в Hibernate представляет собой объект, который может быть сохранен в базе данных и имеет поля, которые соответствуют столбцам таблицы в базе данных. Он также может иметь отношения с другими Entity и используется для выполнения операций CRUD в базе данных.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Каким условиям должен удовлетворять класс чтобы являться Entity?

A

Требования к Entity классу в JPA
1. Entity класс должен быть помечен аннотацией @Entity или описан в XML файле конфигурации JPA.
2. Entity класс должен содержать public или protected конструктор без аргументов (он также может иметь конструкторы с аргументами).
3. Entity класс должен быть классом верхнего уровня (top-level class).
4. Перечисление [enum] или интерфейс [interface] не могут быть определены как сущность [Entity].
5. Entity класс не может быть финальным классом (final class). Entity класс не может содержать финальные поля или методы, если они участвуют в маппинге (persistent final methods or persistent final instance variables).
6. Если объект Entity класса будет передаваться по значению как отделённый от контекста персистентности объект (detached object), например через удаленный интерфейс (through a remote interface), то он также должен реализовывать интерфейс Serializable (чтобы объекты которые достаются из базы могли сохраняться в кэше).
7. Как обычный так и абстрактный класс может быть Entity. Entities могут наследоваться как от не Entity классов, так и от Entity классов. А не Entity классы могут наследоваться от Entity классов.
8. Поля Entity класса должны быть объявлены private, protected или package-private, быть напрямую доступными только методам самого Entity класса и не должны быть напрямую доступны другим классам, использующим этот Entity. Другие классы должны обращаться только к специальным методам Entity класса, предоставляющим доступ к этим полям (getter/setter-методам или другим методам бизнес-логики в Entity классе).
9. Entity класс должен содержать первичный ключ, то есть атрибут или группу атрибутов, которые уникально определяют запись этого Entity класса в базе данных.
10. Должен быть POJO.

Требования к Entity классу в Hibernate
Hibernate не так строг в своих требованиях. Вот отличия от требований JPA:

  • Класс сущности должен иметь конструктор без аргументов, который может быть не только public или protected, но и package visibility (default).
  • Класс сущности не обязательно должен быть классом верхнего уровня.
  • Технически Hibernate может сохранять финальные классы или классы с финальными методами (getter / setter). Однако, как правило, это не очень хорошая идея, так как это лишит Hibernate возможности генерировать прокси для отложенной загрузки сущности.
  • Hibernate не запрещает разработчику приложения открывать прямой доступ к переменным экземпляра и ссылаться на них извне класса сущности.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Что такое Session? Какие функции он выполняет?

A

Session – однопоточный короткоживущий объект, который предоставляет связь между объектами приложения и базой данных. Он оборачивает JDBC java.sql.Connection и работает как фабрика для org.hibernate.Transaction.

Session выполняет следующие функции:

  1. Управление жизненным циклом объектов: Session отслеживает состояние объектов и обеспечивает их сохранение в базе данных при необходимости.
  2. Выполнение запросов: Session позволяет выполнять запросы к базе данных с помощью языка HQL (Hibernate Query Language).
  3. Кэширование данных: Session кэширует данные, чтобы ускорить доступ к ним и уменьшить количество запросов к базе данных.
  4. Управление транзакциями: Session обеспечивает управление транзакциями базы данных и гарантирует целостность данных при выполнении операций.
  5. Работа с отношениями между объектами: Session позволяет работать с отношениями между объектами, такими как связи один-ко-многим и многие-ко-многим.

Экземпляр Session является интерфейсом между кодом в java приложении и hibernate framework и предоставляет методы для операций CRUD. Расширяет интерфейс EntityManager

Когда мы работаем с Hibernate, все операции с объектами выполняются в контексте сессии (Session). Изменения, которые мы вносим в объекты в рамках сессии, могут быть отложены и не записаны в базу данных до тех пор, пока мы явно не вызовем метод session.flush().

Этот метод гарантирует, что все изменения, которые были произведены в рамках текущей транзакции, будут сохранены в базе данных. Если во время выполнения session.flush() возникнет какая-либо ошибка, то транзакция будет откатана и изменения будут отменены.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

что делает метод flush() в Hibernate

A

Метод flush() в Hibernate принудительно сбрасывает все изменения, ожидающие выполнения в текущей сессии, в базу данных. Это означает, что все операции записи, обновления или удаления объектов, которые были сделаны во время текущей транзакции, будут выполнены в базе данных.
Когда вы вызываете flush(), Hibernate проверяет, есть ли у объектов изменения, которые нужно сохранить в базе данных, и если такие изменения есть, они будут отправлены в базу данных немедленно. Таким образом, если вы вызываете flush(), то не нужно ждать завершения транзакции, чтобы увидеть изменения в базе данных.

Важно отметить, что вызов метода flush() не завершает текущую транзакцию и не коммитит изменения в базу данных. Вместо этого метод flush() только записывает все изменения в текущей сессии в базу данных, чтобы они были доступны для других сессий. Flush() также может вызывать события, такие как проверка ограничений на целостность данных и генерация исключений, если какие-либо ограничения были нарушены.

После вызова метода flush() вы можете продолжить работу с объектами в текущей сессии, и ваши изменения будут отражены в базе данных. Однако, если вы хотите завершить транзакцию и сохранить все изменения в базе данных, вам нужно вызвать метод commit() на объекте Transaction.

Обычно вызов flush() не требуется, так как Hibernate автоматически сбрасывает изменения в базу данных при необходимости. Однако, если вы хотите увидеть изменения в базе данных немедленно или нужно выполнить операции записи или обновления в базе данных перед выполнением других действий, то вызов flush() может быть полезным.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Что такое Transaction ? Какие функции он выполняет?

A

Transaction hibernate - это технология, которая используется для управления транзакциями в приложении Hibernate.

Transaction в Hibernate представляет собой логическую единицу работы с базой данных, которая может быть выполнена как единое целое. Транзакция начинается вызовом метода transaction.begin() и заканчивается вызовом метода commit() или rollback(). Транзакция обычно используется для обеспечения целостности данных, при которой все изменения в базе данных происходят в рамках одной транзакции, что гарантирует, что либо все изменения будут сохранены в базе данных, либо ни одного из них.

Transaction в Hibernate выполняет следующие функции:

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

Transaction использует объект Session для управления транзакциями в Hibernate. Каждая транзакция должна быть ассоциирована с конкретной сессией, которая предоставляет доступ к объектам базы данных.

Session session = sessionFactory.openSession();
Transaction tx = null;
try {
    tx = session.beginTransaction();
    // выполнение операций с объектами в базе данных
    tx.commit(); // зафиксировать изменения
} catch (Exception e) {
    if (tx!=null) tx.rollback(); // откатить изменения в случае ошибки
    e.printStackTrace();
} finally {
    session.close();
}

Фиксация транзакции - это процесс сохранения изменений в базе данных после успешного выполнения всех операций, связанных с транзакцией. Transaction Hibernate автоматически фиксирует транзакцию после ее успешного завершения.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Что такое SessionFactory? Какие функции он выполняет?

A

SessionFactory в Hibernate является фабрикой сессий, которая создает объекты Session для работы с базой данных. Он является ключевым компонентом Hibernate и обеспечивает управление жизненным циклом сессий.
SessionFactory отвечает за считывание параметров конфигурации Hibernate и подключение к базе данных.
Обычно в приложении имеется только один экземпляр SessionFactory и потоки, обслуживающие клиентские запросы, получают экземпляры session с помощью объекта SessionFactory. Внутреннее состояние SessionFactory неизменно (immutable). Internal state (внутреннее состояние) включает в себя все метаданные об Object/ Relational Mapping и задается при создании SessionFactory.

SessionFactory выполняет следующие функции:
1. Создание объектов Session: SessionFactory создает новые объекты Session для каждого запроса к базе данных.
2. Управление соединениями: SessionFactory управляет соединениями с базой данных и обеспечивает их оптимальное использование.
3. Кэширование метаданных: SessionFactory кэширует метаданные, такие как информация о таблицах и отображениях объектов, что позволяет ускорить доступ к данным.
4. Поддержка многопоточности: SessionFactory обеспечивает поддержку многопоточности и безопасность при работе с базой данных.
5. Настройка Hibernate: SessionFactory позволяет настраивать различные параметры Hibernate, такие как настройки соединения с базой данных, параметры кэширования и т.д.
6. Управление транзакциями: SessionFactory обеспечивает управление транзакциями базы данных и гарантирует целостность данных при выполнении операций.

Т.к. объект SessionFactory immutable (неизменяемый), то , он потокобезопасный. Множество потоков может обращаться к одному объекту одновременно.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Что такое EntityManager? Какие функции он выполняет?

A

EntityManager интерфейс JPA, который описывает API для всех основных операций над Entity, а также для получения данных и других сущностей JPA.

EntityManager - это интерфейс в рамках технологии Java Persistence API (JPA), который предоставляет API для работы с объектами, хранящимися в базе данных.

Это интерфейс JPA, используемый для взаимодействия с Persistence context.

Persistence context — это среда в которой экземпляры Entity синхронизируются с аналогичными Entity в базе данных. Persistence context является своего рода кэшем данных в рамках транзакции - это и есть кэш первого уровня. Внутри Persistence context происходит управление экземплярами Entity и их жизненным циклом.

Persistence context - это механизм в JPA, который отслеживает изменения объектов в базе данных и управляет их жизненным циклом. Он помогает сохранять данные в базе данных и управлять изменениями в рамках транзакций при помощи EntityManager. Когда мы создаем новый объект или получаем его из базы данных, он добавляется в persistence context и все изменения в объектах автоматически сохраняются в базе данных при завершении транзакции.

EntityManager автоматически сохраняет в БД все изменения, сделанные в его персистентном контексте, в момент коммита транзакции, либо при явном вызове метода flush().

Один или несколько EntityManager образуют или могут образовать persistence context.

Он предоставляет API для выполнения операций с базой данных, таких как создание, чтение, обновление и удаление объектов.
EntityManager описывает API для всех основных операций над Entity, а также для получения данных и других сущностей JPA. По сути - главный API для работы с JPA.

Если проводить аналогию с обычным JDBC, то EntityManagerFactory будет аналогом DataSource, а EntityManager аналогом Connection.

Основные функции EntityManager:
1) Операции над Entity: persist (добавление Entity), merge (обновление), remove (удаления), refresh (обновление данных), detach (удаление из управление JPA), lock (блокирование Entity от изменений в других thread),
2) Получение данных: find (поиск и получение Entity), createQuery, createNamedQuery, createNativeQuery, contains, createNamedStoredProcedureQuery, createStoredProcedureQuery
3) Получение других сущностей JPA: getTransaction, getEntityManagerFactory, getCriteriaBuilder, getMetamodel, getDelegate
4) Работа с EntityGraph: createEntityGraph, getEntityGraph
5) Общие операции над EntityManager или всеми Entities: close, clear, isOpen, getProperties, setProperty.
6) Выполнение запросов: EntityManager позволяет выполнять запросы к базе данных с помощью языка JPQL (Java Persistence Query Language).
7) Управление транзакциями: EntityManager обеспечивает управление транзакциями базы данных и гарантирует целостность данных при выполнении операций.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Что такое Persistence context

A

Persistence context он же first level Cash

PC является проекцией на нашу текущую БД а точнее на ее состояние в данный момент времени.

У каждой Session есть свой Persistence context
Persistence context - это контекст, в котором находятся все объекты, которые были получены из базы данных или созданы при помощи EntityManager. Он является частью EntityManager и предоставляет механизм отслеживания изменений объектов и обеспечивает управление жизненным циклом сущностей и их сохранением в базе данных.

Persistence context (контекст сохранения) - это среда, в которой происходит взаимодействие между объектами Java и базой данных в рамках Java Persistence API (JPA). Контекст сохранения представляет собой набор управляемых объектов сущностей, которые находятся в состоянии persistent (постоянном), то есть связанных с базой данных и готовых к сохранению или обновлению. Контекст сохранения отслеживает изменения объектов и автоматически синхронизирует их с базой данных при вызове метода commit() или при завершении транзакции. Контекст сохранения также обеспечивает кэширование объектов и оптимизацию запросов к базе данных.

очистить кэш можно используя методы:
* evict(user); - удаляем объект из кэша
* clear(); - очистка мапы
* close(); - закрыть сессию

Persistence context:
* Набор управляемых объектов сущностей, которые были получены из базы данных или созданы в рамках приложения.
* Кэш первого уровня, который содержит копии объектов сущностей, полученных из базы данных. Это позволяет избежать повторных запросов к базе данных при повторном доступе к объекту.
* Кэш второго уровня, который содержит копии объектов сущностей, полученных из базы данных. Это позволяет избежать повторных запросов к базе данных при повторном доступе к объекту на других уровнях приложения.

Например, если мы создадим новый объект с помощью метода persist(), он будет добавлен в persistence context. В дальнейшем, любые изменения, внесенные в этот объект, будут отслеживаться EntityManager и автоматически сохраняться в базе данных при завершении транзакции.

Кроме того, если мы получим объект из базы данных при помощи метода find(), он также будет добавлен в persistence context. При последующих вызовах методов EntityManager для этого объекта будет использован именно этот экземпляр из persistence context.

Примеры использования Persistence context:

  1. Создание нового объекта и его добавление в persistence context:
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();

Employee employee = new Employee();
employee.setName("John");

em.persist(employee);

em.getTransaction().commit();
em.close();

В этом примере мы создаем нового сотрудника и добавляем его в persistence context при помощи метода persist(). Затем при завершении транзакции все изменения будут автоматически сохранены в базе данных.

  1. Изменение объекта, полученного из базы данных:
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();

Employee employee = em.find(Employee.class, 1L);
employee.setName("Jane");

em.getTransaction().commit();
em.close();

В этом примере мы получаем объект Employee из базы данных при помощи метода find() и изменяем его имя. Так как объект находится в persistence context, изменения будут автоматически сохранены в базе данных при завершении транзакции.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Схема работы HIbernate

A

Конечная цель Hibernate - сохранение данных в базе данных, и Session является основным объектом для работы с Hibernate. Он предоставляет API для выполнения CRUD (Create, Read, Update, Delete) операций с базой данных.

Ниже приведен пример использования SessionFactory и Transaction в Hibernate:

// Инициализация конфигурации Hibernate
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");

// Создание объекта SessionFactory из конфигурации Hibernate
SessionFactory sessionFactory = configuration.buildSessionFactory();

// Получение объекта Session из SessionFactory
Session session = sessionFactory.openSession();

// Создание объекта транзакции
Transaction transaction = session.beginTransaction();

try {
    // Выполнение операций сохранения, чтения, обновления или удаления объектов
    // ...

    // Закрываем транзакцию
    transaction.commit();
} catch (Exception e) {
    // Откатываем транзакцию в случае возникновения ошибки
    transaction.rollback();
} finally {
    // Закрываем объект Session
    session.close();
}

В этом примере мы создаем конфигурацию Hibernate, инициализируем ее из файла hibernate.cfg.xml и создаем объект SessionFactory. Затем мы открываем объект Session из SessionFactory и создаем транзакцию для выполнения операций с базой данных. Внутри транзакции мы можем выполнить операции с объектами, которые находятся в управляемом состоянии (например, сохранение, загрузка, обновление, удаление). После выполнения операций мы фиксируем изменения в базе данных с помощью tx.commit(). Если происходит исключение, мы откатываем транзакцию и закрываем сессию.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

как между собой взаимодействуют entitymanager , persistence context, Session, session factory и transaction в hibernate?

A

EntityManager и Session - это два инструмента, которые используются для работы с базами данных в Hibernate. EntityManager является частью Java Persistence API (JPA), а Session - частью Hibernate API.

Persistence context и Session - это контексты, в которых находятся все объекты, которые были получены из базы данных или созданы. Они предоставляют механизм отслеживания изменений объектов и управления их жизненным циклом. EntityManager и Session обеспечивают управление этими контекстами и объектами.

EntityManager и Persistence Context:
EntityManager является аналогом Session в JPA. Persistence Context - это контекст персистентности, который управляет объектами, хранящимися в базе данных. Каждый EntityManager имеет свой Persistence Context, который содержит все объекты, загруженные из базы данных и управляемые этим EntityManager.

Session и EntityManager являются интерфейсами, которые предоставляют способы управления состоянием объектов, связи с базой данных и выполнения операций, таких как сохранение, загрузка, обновление и удаление.

SessionFactory является фабрикой сессий (Session). Сессия представляет собой механизм для выполнения операций с базой данных в рамках определенной транзакции. При создании сессии, Hibernate создает новый Persistence Context для этой сессии. Persistence Context - это контейнер, который содержит все объекты, загруженные из базы данных и управляемые этой сессией.

Transaction - это транзакция, которая используется для управления изменениями в базе данных. Она обеспечивает контроль над целостностью данных, позволяет откатывать изменения в базе данных при необходимости и устраняет проблемы с параллельным доступом к данным.
Транзакция - это механизм для группировки нескольких операций в базе данных в одну атомарную единицу работы. В Hibernate транзакции начинаются и заканчиваются с помощью методов begin() и commit() соответственно.

Transaction взаимодействует с EntityManager/Session для контроля транзакций.

Таким образом, взаимодействие между EntityManager, Persistence context, Session, Session Factory и Transaction в Hibernate осуществляется следующим образом:

  1. Session Factory используется для создания сессий (Session).
  2. Session используется для выполнения операций над объектами, хранящимися в базе данных.
  3. EntityManager и Persistence Context управляют жизненным циклом объектов и обеспечивают автоматическое сохранение изменений в базе данных.
  4. Transaction используется для управления транзакцией и обеспечения контроля над целостностью данных.

В целом, все эти компоненты работают вместе для обеспечения эффективной работы с базами данных в Hibernate.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Может ли абстрактный класс быть Entity?

A

В спецификации JPA в пункте 2.1 The Entity Class есть строка: “И абстрактные, и конкретные классы могут быть сущностями”. То есть, ответ — да, абстрактный класс может быть сущностью и может быть аннотирован с помощью @Entity.
Может, при этом он сохраняет все свойства Entity, за исключением того, что его нельзя непосредственно инициализировать.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Может ли Entity класс наследоваться от не Entity классов (non-entity classes)?

A

Да, сущности могут наследоваться от не Entity классов, которые, в свою очередь, могут быть как абстрактными, так и обычными. Состояние (поля) не Entity суперкласса не является персистентным, то есть не хранится в БД и не обрабатывается провайдером (Hibernate), поэтому любое такое состояние (поля), унаследованное Entity классом, также не будет отображаться в БД.

Да, Entity класс может наследоваться от non-entity классов, так как наследование не влияет на то, удовлетворяет ли класс условиям Entity. Главное, чтобы сам класс удовлетворял всем условиям Entity.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Может ли Entity класс наследоваться от других Entity классов?

A

Да, Entity класс может наследоваться от других Entity классов.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Может ли не Entity класс наследоваться от Entity класса?

A

Да, может.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Что такое встраиваемый (Embeddable) класс?

A

Embeddable-класс – это класс, который не используется сам по себе, а является частью одного или нескольких entity-классов.
Во время выполнения каждый встраиваемый класс принадлежит только одному объекту entity-класса и не может быть использован для передачи данных между объектами entity-классов (то есть такой класс не является общей структурой данных для разных объектов).

Встраиваемый класс помечается аннотацией @Embeddable.

Embeddable-класс может содержать другой встраиваемый класс. Entity класс могут содержать как одиночные встраиваемые классы, так и коллекции таких классов.

Например, если у нас есть класс “Адрес”, который содержит в себе поля “город”, “улица” и “зипкод”, то мы можем создать встраиваемый класс “Адрес”, который будет содержать эти же поля. Этот класс может быть использован в других классах, например, в классе “Пользователь”, который будет содержать в себе информацию о пользователе и его адресе.

@Embeddable
public class Address {
    @Column(name = "street")
    private String street;
    
    @Column(name = "city")
    private String city;
    
    @Column(name = "zip_code")
    private String zipCode;
    
    // геттеры и сеттеры
    // ...
}
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "name")
    private String name;
    
    @Embedded
    private Address address;
    
    // геттеры и сеттеры
    // ...
}

В этом примере класс Address является встраиваемым классом, содержащим три атрибута: street, city и zipCode. Этот класс может быть использован как атрибут в других сущностях для представления адреса объекта.

Особенности встраиваемых классов

  • все поля встраиваемого класса, даже коллекции, станут полями класса, в который происходит встраивание;
  • встраиваемые классы могут быть встроены в одну и ту же сущность несколько раз, нужно только поменять имена полей;
  • экземпляры встраиваемых классов, в отличие от экземпляров сущностей, не имеют собственного персистентного состояния, вместо этого они существуют только как часть состояния объекта, которому они принадлежат;
  • встраиваемые классы могут использовать в качестве полей:
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Какие требования JPA
устанавливает к встраиваемым (Embeddable) классам?

A

Требования к встраиваемым классам:
* Должны соответствовать требованиям для сущностей (раздел 2.1 Java Persistence API), за исключением того, что у встраиваемых классов не ставится аннотация @Entity и может отсутствовать первичный ключ (@Id).
* Должны быть аннотированы @Embeddable.
* Класс, содержащий встраиваемый класс, должен содержать поле с типом этого класса.
* Встраиваемый класс не может иметь своего собственного идентификатора, он наследует идентификатор из класса, в котором он используется.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Что такое Mapped Superclass?

A

Mapped Superclass это класс от которого наследуются Entity, он может содержать аннотации JPA, однако сам такой класс не является Entity, ему не обязательно выполнять все требования установленные для Entity (например, он может не содержать первичного ключа). Такой класс не может использоваться в операциях EntityManager или Query. Такой класс должен быть отмечен аннотацией MappedSuperclass или соответственно описан в xml файле.

В контексте фреймворка JPA (Java Persistence API), Mapped Superclass - это способ повторного использования общих полей и методов среди нескольких сущностей в рамках иерархии наследования. Он используется для выноса общих полей и методов из сущностей в отдельный класс, который не является сущностью, но может быть унаследован другими сущностями.

Использование Mapped Superclass позволяет сократить дублирование кода при определении сущностей и упростить обслуживание при изменении общих полей или методов.

Особенности:
- Должен быть помечен аннотацией @MappedSuperclass или описан в xml файле. - Не может использоваться в операциях EntityManager или Query, вместо этого нужно использовать классы-наследники.
- Не может состоять в отношениях с другими сущностями (в сущности нельзя создать поле с типом сопоставленного суперкласса).
- Может быть абстрактным.
- Не имеет своей таблицы в БД.

Для того, чтобы использовать Mapped Superclass, достаточно унаследовать его в классах-потомках:
~~~
@MappedSuperclass
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
………….
}
@Entity
@Table(name = “FULL_TIME_EMP”)
public class FullTimeEmployee extends Employee {
private int salary;
………….
}
@Entity
@Table(name = “PART_TIME_EMP”)
public class PartTimeEmployee extends Employee {
private int hourlyRate;
………….
}
~~~

В указанном примере кода в БД будут таблицы FULLTIMEEMPLOYEE и PARTTIMEEMPLOYEE, но таблицы EMPLOYEE не будет:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Mapped Superclass vs. Embeddable class

A

Mapped Superclass — это класс сущности, который предоставляет информацию о сопоставлении для своих подклассов, но не может быть создан сам по себе.

Embeddable class — это класс, не являющийся сущностью, который представляет собой набор свойств, которые являются общими для нескольких классов сущностей и могут быть внедрены в таблицы или столбцы этих сущностей.

Сходства:

  • не являются сущностями и могут иметь все аннотации, кроме @Entity;
  • не имеют своих таблиц в БД;
  • не могут использоваться в операциях EntityManager или Query.
    Различия:
  • MappedSuperclass - наследование, Embeddable class- композиция (экземпляр «части» может входить только в одно целое (или никуда не входить));
  • поля из Mapped Superclass могут быть у сущности в одном экземпляре, полей из Embeddable class может быть сколько угодно (встроив в сущность Embeddable class несколько раз и поменяв имена полей);
  • в сущности нельзя создать поле с типом сопоставленного суперкласса, а с Embeddable можно и нужно.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

Какие три типы стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA?

A

Стратегии наследования нужны для того, чтобы дать понять провайдеру (Hibernate) как ему отображать в БД сущности-наследники. Для этого нам нужно декорировать родительский класс аннотацией @Inheritance и указать один из типов отображения: SINGLE_TABLE, TABLE_PER_CLASS, JOINED.

SINGLE_TABLE. Одна таблица на всю иерархию классов.
TABLE_PER_CLASS. Таблица для каждого конкретного класса сущностей.
JOINED. Стратегия «соединения», при которой поля или свойства, специфичные для подклассов, отображаются в таблицах этих подклассов, а поля или свойства родительского класса отображаются в таблице родительского класса.

@Inheritance(strategy = InheritanceType.JOINED)
@Entity
@DiscriminatorColumn(name = "EMP_TYPE") //определение типа наследника

Одна таблица на всю иерархию классов (SINGLE TABLE)
Является стратегией по умолчанию и используется, когда аннотация @Inheritance не указана в родительском классе или когда она указана без конкретной стратегии. все enity, со всеми наследниками записываются в одну таблицу, для идентификации типа entity определяется специальная колонка “discriminator column”.

Существует проблема денормализации таблицы, так как все данные хранятся в одной таблице.

Например, если есть entity Animals c классами-потомками Cats и Dogs, при такой стратегии все entity записываются в таблицу Animals, но при это имеют дополнительную колонку animalType в которую соответственно пишется значение «cat» или «dog».Минусом является то что в общей таблице, будут созданы все поля уникальные для каждого из классов-потомков, которые будет пусты для всех других классов-потомков. Например, в таблице animals окажется и скорость лазанья по дереву от cats и может ли пес приносить тапки от dogs, которые будут всегда иметь null для dog и cat соответственно.
объединяющая стратегия (joined subclass strategy)в этой стратегии каждый класс enity сохраняет данные в свою таблицу, но только уникальные колонки (не унаследованные от классов-предков) и первичный ключ, а все унаследованные колонки записываются в таблицы класса-предка, дополнительно устанавливается связь (relationships) между этими таблицами,
например в случае классов Animals (см.выше), будут три таблицы animals, cats, dogs, причем в cats будет записана только ключ и скорость лазанья, в dogs — ключ и умеет ли пес приносить палку, а в animals все остальные данные cats и dogs c ссылкой на соответствующие таблицы. Минусом тут являются потери производительности от объединения таблиц (join) для любых операций.
одна таблица для каждого класса (table per concrete class strategy)
каждый отдельный класс-наследник имеет свою таблицу в которой присутствуют в т.ч. унаследованные поля. ТАблица предка не создаётся.
каждый отдельный класс-наследник имеет свою таблицу в которой присутствуют в т.ч. унаследованные поля. ТАблица предка не создаётся.
Минусы
- плохая поддержка полиморфизмаи
- для выборки всех классов иерархии потребуются большое количество отдельных sql-запросов или использование UNION-запроса.
- Не можем использвать стратегию генерации ключа “IDENTITY”
- Снижение производительности, из за большого количества joino`в

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

Пример реализации стратегии

A
@Entity
@Table(name = "employees")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type")
public abstract class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // общие поля для всех сущностей

    // геттеры и сеттеры
}

@Entity
@DiscriminatorValue("manager")
public class Manager extends Employee {
    private String department;
    // поля для сущности Manager

    // геттеры и сеттеры
}

@Entity
@DiscriminatorValue("developer")
public class Developer extends Employee {
    private String programmingLanguage;
    // поля для сущности Developer

    // геттеры и сеттеры
}

В этом примере сущности Manager и Developer наследуются от абстрактного класса Employee с помощью стратегии Single Table. В таблице employees хранятся все поля всех сущностей, а для отличия объектов друг от друга используется столбец employee_type. С помощью аннотации @DiscriminatorValue указывается значение этого столбца для каждой сущности.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

Как мапятся Enumы?

A
  • @Enumerated(EnumType.STRING) означает, что в базе будут храниться имена Enum. Всё сломается при переименовании Enum
  • **@Enumerated(EnumType.ORDINAL) **– в базе будут храниться порядковые номера Enum. Всё сломается при добавленнии или изменении порядка значений. Используется по-умолчанию.
  • Можно смапить enum в БД и обратно в методах с аннотациями **@PostLoad и @PrePersist. @EntityListener **над классом Entity, где указать класс, в котором создать два метода, помеченных этими аннотациями. Идея в том, чтобы в сущности иметь не только поле с Enum, но и вспомогательное поле. Поле с Enum аннотируем @Transient, а в БД будет храниться значение из вспомогательного поля. Нельзя иметь 2 Enum с одинаковыми атрибутами. и использовать JPQL
  • В JPA с версии 2.1 можно использовать Converter для конвертации Enum’а в некое его значение для сохранения в БД и получения из БД. Нужно лишь создать новый класс, который реализует javax.persistence.AttributeConverter и переписать 2 метода
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

Как мапятся даты (до Java 8 и после)?

A

До Java 8 даты могли быть маппированы в базу данных с помощью классов java.util.Date или java.sql.Date. или java.util.Calendar
Для этого в JPA используется аннотация @Temporal, которая указывает, какой тип данных должен быть использован при сохранении даты в базе данных.

После Java 8 появился новый пакет java.time, который содержит новые классы для работы с датами и временем. В JPA 2.2 была добавлена поддержка этих классов с помощью аннотации @Convert, которая позволяет указать конвертер, который будет использоваться для преобразования значений между Java-кодом и базой данных.

До Java 8 даты мапятся с помощью класса java.util.Date или его наследников (например, java.sql.Date или java.sql.Timestamp). В Hibernate используются аннотации @Temporal и @Column для указания типа данных и формата хранения даты в базе данных.

Пример маппинга даты до Java 8:

@Entity
public class Order {
    @Id
    private Long id;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "order_date")
    private Date orderDate;
}

После Java 8 для маппинга дат используется новый класс java.time.LocalDate, java.time.LocalTime и java.time.LocalDateTime. В Hibernate также используются аннотации @Temporal и @Column для указания типа данных и формата хранения даты в базе данных.

Пример маппинга даты после Java 8:

@Entity
public class Order {
    @Id
    private Long id;
    @Column(name = "order_date")
    private LocalDateTime orderDate;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q

Как “смапить” коллекцию примитивов?

A

Для маппинга коллекции примитивных типов данных в Hibernate следует использовать аннотацию @ElementCollection.

Если у сущности коллекция базовых или встраиваемых (embeddable) типов, то для этих случаев в JPA имеется специальная аннотация @ElementCollection, которая указывается в классе сущности над полем коллекции. Тогда все записи коллекции хранятся в отдельной таблице.
При добавлении новой строки в коллекцию она полностью очищается и заполняется заново, так как у элементов нет id.
Если нужно сохранять порядок элементов в коллекции, то можно использовать аннотацию @OrderColumn:
~~~
@ElementCollection
@OrderColumn(name = “position”)
private List<Integer> myIntegers = new ArrayList<>();
~~~</Integer>

Если нужно использовать другое имя таблицы для коллекции, то можно использовать аннотацию @CollectionTable:
~~~
@ElementCollection
@CollectionTable(name = “my_integers_table”)
private List<Integer> myIntegers = new ArrayList<>();
~~~
```
@Entity
public class User {
@Id
@GeneratedValue
private Long id;</Integer>

private String name;

@ElementCollection
private Set<String> emails;

// геттеры и сеттеры } ~~~ В этом примере поле emails отображается с помощью аннотации @ElementCollection, что означает, что это коллекция элементов, которые не являются сущностями. Hibernate автоматически создаст таблицу User_emails, которая будет содержать колонки User_id и emails, которые будут соответствовать полю id сущности User и элементам коллекции emails соответственно.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q

Какие есть виды связей в Hibernate?

A

@OneToOne -когда один экземпляр Entity может быть связан не больше чем с одним экземпляром другого Entity.
@OneToMany - когда один экземпляр Entity может быть связан с несколькими экземплярами других Entity.
@ManyToOne - обратная связь для OneToMany. Несколько экземпляров Entity могут быть связаны с одним экземпляром другого Entity.
@ManyToMany - экземпляры Entity могут быть связаны с несколькими экземплярами друг друга.
Кроме того, в Hibernate также существует возможность использования дополнительных атрибутов для связей, таких как @JoinColumn, @JoinTable и @MappedBy. Они позволяют настроить более сложные отношения между сущностями.

Каждую группу можно дополнительно разделить на
1. Bidirectional с использованием @MappedBy * на стороне, где указывается @OneToMany
2. Unidirectional. без использования @mappedBy (Hibernate создаёт смежную таблицу, в которой связывает значениях таблиц. Что ухудшает производительность.)

Bidirectional — ссылка на связь устанавливается у всех Entity, то есть в случае OneToOne A-B в Entity A есть ссылка на Entity B, в Entity B есть ссылка на Entity A. Entity A считается владельцем этой связи (это важно для случаев каскадного удаления данных, тогда при удалении A также будет удалено B, но не наоборот).

Undirectional- ссылка на связь устанавливается только с одной стороны, то есть в случае OneToOne A-B только у Entity A будет ссылка на Entity B, у Entity B ссылки на A не будет.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q

@OneToOne

A
@Entity
public class Employee {
    @Id
    private Long id;
    
    @OneToOne
    private Address address;
    
    // ... getters/setters
}

@Entity
public class Address {
    @Id
    private Long id;
    
    @OneToOne(mappedBy = "address")
    private Employee employee;
    
    // ... getters/setters
}
33
Q

@OneToMany

A
@Entity
public class Team {
    @Id
    private Long id;
    
    @OneToMany(mappedBy = "team")
    private List<Player> players;
    
    // ... getters/setters
}

@Entity
public class Player {
    @Id
    private Long id;
    
    @ManyToOne
    private Team team;
    
    // ... getters/setters
}``
34
Q

@ManyToOne

A
@Entity
public class Order {
    @Id
    private Long id;
    
    @ManyToOne
    private Customer customer;
    
    // ... getters/setters
}

@Entity
public class Customer {
    @Id
    private Long id;
    
    @OneToMany(mappedBy = "customer")
    private List<Order> orders;
    
    // ... getters/setters
}
35
Q

@ManyToMany

A
@Entity
public class Book {
    @Id
    private Long id;
    
    @ManyToMany(mappedBy = "books")
    private List<Author> authors;
    
    // ... getters/setters
}

@Entity
public class Author {
    @Id
    private Long id;
    
    @ManyToMany
    private List<Book> books;
    
    // ... getters/setters
}
36
Q

Что такое владелец связи?

A

В отношениях между двумя сущностями всегда есть одна владеющая сторона, а владеемой может и не быть, если это однонаправленные отношения.
По сути, у кого есть внешний ключ на другую сущность - тот и владелец связи. То есть, если в таблице одной сущности есть колонка, содержащая внешние ключи от другой сущности, то первая сущность признаётся владельцем связи, вторая сущность - владеемой.
В однонаправленных отношениях сторона, которая имеет поле с типом другой сущности, является владельцем этой связи по умолчанию.

В контексте двунаправленной связи между объектами в Hibernate есть понятие “владельца связи”. Это объект, который содержит ссылку на другой объект, и который является ответственным за управление этой связью. Таким образом, если вы хотите установить связь между двумя объектами в Hibernate, вы должны определить, какой из них будет “владельцем связи”.

Владелец связи определяется с помощью аннотации @JoinColumn в классе сущности Hibernate. Эта аннотация определяет имя столбца в таблице базы данных, который будет использоваться для хранения ссылки на другой объект. Таким образом, при изменении объекта, который является владельцем связи, Hibernate автоматически обновит соответствующую запись в базе данных.

Допустим, у нас есть две сущности - Author и Book. Каждая книга имеет только одного автора, но каждый автор может иметь множество книг. Мы хотим установить двунаправленную связь между этими сущностями, и определить автора как владельца связи.

Вот как может выглядеть код для класса Author:

@Entity
@Table(name = "authors")
public class Author {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Long id;
  @Column(name = "name")
  private String name;
  @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
  private List<Book> books = new ArrayList<>();
}

Здесь мы определили отношение “один-ко-многим” между Author и Book с помощью аннотации @OneToMany. Мы указали, что связь между этими сущностями устанавливается через поле “author” в сущности Book, с помощью параметра “mappedBy”. Таким образом, книги будут ссылаться на автора, а автор не будет ссылаться на свои книги.

В классе Book мы определяем внешний ключ для связи с автором с помощью аннотации @JoinColumn:

@Entity
@Table(name = "books")
public class Book {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Long id;
  @Column(name = "title")
  private String title;
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "author_id", referencedColumnName = "id")
  private Author author;
  // getters and setters
}

Здесь мы определили отношение “многие-к-одному” между Book и Author с помощью аннотации @ManyToOne. Мы указали, что владельцем связи является сущность Author, с помощью параметра @JoinColumn. Таким образом, при изменении автора, Hibernate автоматически обновит соответствующие записи в таблице книг.

37
Q

Что такое каскадные операции?

A

Сущности, между которыми есть отношения, часто зависят от существования друг друга. Например, позиции (LineItem) являются частью заказа (CustomerOrder), и если заказ удален, все позиции также должны быть удалены. Это называется каскадным удалением.
JPA позволяет распространять операции с сущностями (например, persist или remove) на связанные сущности. Это означает, что при включенном каскадировании, если сущность A сохраняется или удаляется, тогда сущность B (связанная с A отношением, например через ManyToOne) также будет сохраняться или удаляться без явных команд сохранения или удаления.

Каскадные операции позволяют выполнять операции сохранения, обновления или удаления на одной из сущностей и автоматически применять эти операции к связанным сущностям.

1) Каскадные связи - определение, когда изменение состояния объекта класса должно распространяться на его внутренний (ассоциированный) объект.
2) Например, если сущность User ссылается на сущность Photo - то при каких операциях нужно сохранять/удалять/обновлять записи об объекте Photo при действиях в объекте User.

Каскадирования можно добиться, указав у любой из аннотаций @OneToOne, @ManyToOne, @OneToMany, @ManyToMany элемент cascade и присвоив ему одно или несколько значений из перечисления CascadeType (ALL, DETACH, MERGE, PERSIST, REFRESH, REMOVE).

Каскадирование – это когда действие с целевой Entity будет применено к связанной Entity.

JPA CascadeType:
* CASCADE_ALL - все операции (создание, обновление, удаление) применяются к связанным сущностям.
* CASCADE_PERSIST - операции создания(save() или persist() ) применяются к связанным сущностям.
* CASCADE_MERGE - операции обновления применяются к связанным сущностям.
* CASCADE_REMOVE - операции удаления применяются к связанным сущностям.
* CASCADE_REFRESH - операции обновления (загрузки из базы данных) применяются к связанным сущностям.
* CASCADE_DETACH - отключает все связанные entity, если происходит «ручное отключение»

Hibernate CascadeType:
- REPLICATE - копирует состояние сущности на другой узел кластера;
- SAVE_UPDATE - сохраняет или обновляет связанные сущности;
- LOCK - блокирует связанные сущности во время блокировки основной сущности.
Каскадные операции позволяют упростить и ускорить работу с базой данных, так как не требуется выполнять отдельные операции на каждой связанной сущности.

38
Q

Удаление сирот в отношениях (Orphan Removal)

A

Удаление сирот в отношениях (Orphan Removal) - это механизм в фреймворке Hibernate, который позволяет автоматически удалять дочерние объекты, когда они больше не связаны с родительским объектом. Это означает, что если вы удаляете родительский объект, все его дочерние объекты также будут удалены из базы данных.

Чтобы включить удаление сирот в отношениях, необходимо использовать аннотацию @OneToMany или @OneToOne, а также добавить параметр orphanRemoval=true. Пример:
~~~
@Entity
public class Parent {
@OneToMany(mappedBy = “parent”, orphanRemoval = true)//// KEY ANNOTATION
private List<Child> children;
// ...
}</Child>

@Entity
public class Child {
@ManyToOne
private Parent parent;
// …
}
~~~

В этом примере, если вы удалите родительский объект (Parent), все его дочерние объекты (Child) будут автоматически удалены из базы данных. Однако, если вы удалите дочерний объект (Child), родительский объект (Parent) останется в базе данных без изменений.

Этот механизм может быть полезен для обеспечения целостности данных в базе данных и упрощения кода, но следует использовать его с осторожностью, чтобы избежать случайного удаления данных, которые все еще нужны.

39
Q

что делает метод session.find()?

A

Метод session.find() используется в Hibernate для получения объекта из базы данных по его первичному ключу. Этот метод является одним из способов получения объекта в Hibernate и может использоваться для получения объекта, когда известен его первичный ключ.
~~~
public <T> T find(Class<T> entityClass, Object primaryKey);
~~~</T></T>

Например, если у нас есть класс Person с первичным ключом типа Long, мы можем получить объект Person из базы данных следующим образом:
~~~
Long personId = 1L;
Person person = session.find(Person.class, personId);
~~~

Важно отметить, что метод session.find() возвращает null, если объект не найден в базе данных.

40
Q

Метод getReference() в Hibernate.

A

Метод getReference() в Hibernate возвращает “ленивую ссылку” на объект в базе данных, а не сам объект. “Ленивая ссылка” - это специальный объект-заглушка, который содержит только идентификатор объекта, но не загружает его из базы данных до тех пор, пока не будет вызван какой-либо метод этого объекта.

Использование метода getReference() позволяет избежать лишней загрузки данных из базы данных в случаях, когда мы хотим получить только ссылку на объект, но не сам объект целиком.

Также, метод getReference() может быть полезен, когда мы хотим проверить, существует ли объект с определенным идентификатором в базе данных, без необходимости загружать его целиком. Если объекта с таким идентификатором в базе данных не существует, метод getReference() вернет исключение ObjectNotFoundException.

41
Q

Разница между PERSIST и MERGE?

A

persist(entity) следует использовать с новыми объектами, чтобы добавить их в БД (если объект уже существует в БД, будет выброшено исключение EntityExistsException).
Если использовать merge(entity), то сущность, которая уже управляется в контексте персистентности, будет заменена новой сущностью (обновленной), и копия этой обновленной сущности вернется обратно. Рекомендуется использовать для уже сохраненных сущностей.

В общем, разница между persist() и merge() заключается в том, что persist() всегда сохраняет новую сущность, в то время как merge() либо обновляет существующую сущность, либо создает новую сущность, если она не найдена.
Кроме того, persist() не возвращает никакого значения, в то время как merge() возвращает объект, который представляет обновленную или новую сущность.

42
Q

Разница между PERSIST и SAVE?

A

session.save(): 1) “Оригинальный” метод Hibernate 2) гарантирует генерацию id 3) возвращает id (как Object).
session.persist(): 1) Метод спецификации JPA 2) Не гарантирует создания id 3) Ничего не возвращает.

Методы save() и persist() в Hibernate выполняют одну и ту же функцию - сохранение объекта в базе данных. Однако, есть несколько отличий между ними:

  1. Метод save() возвращает идентификатор сохраненного объекта, тогда как метод persist() ничего не возвращает.
  2. Метод save() можно вызывать на любом объекте, даже если он уже находится в состоянии persistent, тогда как метод persist() может быть вызван только на transient-объектах.
  3. Метод save() генерирует исключение, если попытаться сохранить объект, который уже существует в базе данных (имеет непустой идентификатор), тогда как метод persist() просто игнорирует такой объект.
  4. Метод save() использует генерацию идентификаторов на стороне клиента, тогда как метод persist() использует генерацию идентификаторов на стороне сервера базы данных.

Таким образом, разница между persist() и save() заключается в том, что persist() привязан к жизненному циклу контекста персистентности, а save() - к жизненному циклу транзакции базы данных. Метод persist() не гарантирует сохранение в базу данных, пока транзакция не будет зафиксирована, в то время как метод save() сохраняет объект в базу данных сразу же.
Оба метода являются аналогами операции INSERT в SQL

43
Q

Какие два типа fetch стратегии в JPA вы знаете?

A

В JPA описаны два типа fetch-стратегии:
fetch-стратегии используются для управления тем, как ассоциированные сущности и коллекции будут извлекаться из базы данных.

LAZY — данные поля сущности будут загружены только во время первого обращения к этому полю.
EAGER — данные поля будут загружены немедленно вместе с сущностью.

**FetchType.EAGER: **
Eager Fetch (жадная загрузка) - при использовании этой стратегии, все связанные сущности и коллекции будут загружены из базы данных немедленно вместе с основной сущностью. Это означает, что все связанные сущности будут доступны сразу после загрузки основной сущности, и не будет необходимости делать дополнительные запросы к базе данных для загрузки связанных данных.
Hibernate должен сразу загрузить соответствующее аннотированное поле или свойство. Это поведение по умолчанию для полей, аннотированных @Basic, @ManyToOne и @OneToOne (все что быстро).

FetchType.LAZY:
Lazy Fetch (ленивая загрузка) - при использовании этой стратегии, связанные сущности или коллекции будут загружены из базы данных только при запросе к ним. Например с помощью getter`а

Если требуется явно инициализировать связанный объект, чтобы получить доступ к его свойствам или методам без запроса к базе данных. Для этого используется метод Hibernate.initialize(), который запрашивает у Hibernate загрузку связанного объекта из базы данных и его инициализацию.
~~~
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
// получаем объект Person по его id
Person person = (Person) session.get(Person.class, 1L);
// явно инициализируем коллекцию связанных объектов Address
Hibernate.initialize(person.getAddresses());
tx.commit();
~~~
В этом примере мы получаем объект Person по его id, а затем явно инициализируем его коллекцию связанных объектов Address с помощью метода Hibernate.initialize().

Таким образом, если ассоциированная сущность или коллекция не будет использоваться в рамках текущей транзакции, то Hibernate не будет загружать их в память, что может улучшить производительность приложения.

Hibernate может загружать данные не сразу, а при первом обращении к ним, но так как это необязательное требование, то Hibernate имеет право изменить это поведение и загружать их сразу. Это поведение по умолчанию для полей, аннотированных @OneToMany, @ManyToMany и @ElementCollection (все что медленно) .

@OneToOne  Eager
@OneToMany Lazy
@ManyToOne Eager
@ManyToMany Lazy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = “user_id”, nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonBackReference
@NotNull(groups = View.Persist.class)
private User user;
44
Q

Что такое Hibernate Proxy

A

Hibernate proxy - это объект-заместитель, который создается Hibernate для отложенной загрузки связанных сущностей в JPA. Это позволяет оптимизировать производительность при работе с базой данных, загружая только те связанные сущности, которые действительно нужны, когда они нужны.
Создание Hibernate proxy происходит автоматически при наличии связи между сущностями, которая настроена для отложенной загрузки.
Другими словами, Hibernate proxy - это объект-заглушка, который имеет тот же интерфейс, что и связанная сущность, но не содержит реальных данных. Вместо этого, при обращении к свойствам или методам Hibernate proxy, он отправляет запрос в базу данных для получения необходимых данных. Таким образом, реальные данные будут загружены только в тот момент, когда они действительно нужны, что позволяет уменьшить количество запросов к базе данных и ускорить работу приложения.

45
Q

Какие четыре статуса жизненного цикла Entity-объекта (Entity Instance’s Life Cycle) вы можете перечислить?

A

У Entity объекта существует четыре статуса жизненного цикла:

new (Transient) — объект создан, но при этом ещё не имеет сгенерированных первичных ключей и пока ещё не сохранен в базе данных, не привязан к текущему контексту персистентности (к сессии и к кэшу первого уровня). NB: NB: если генерация id для сущности производится базой данных - объекты в состоянии Transient не должны содержать никиаких значений в поле id

managed (Persistent) — объект создан и управляется JPA, хранится в persistent context и имеет сгенерированные первичные ключи.
В это состояние может попасть объект из абсолютно любого состояния:
- из состояния Transient - методом persist() или save().
- из состояния Detached - метод merge()
- если ассоциированная с объектом запись была удалена, и объект находится в состоянии Removed - методом persist(), чтобы снова сохранить данные в базу, а объект перевести в состояния Persistent.
- Когда мы получаем объект из БД с помощью Hibernate, объект тоже может быть Persistent
Person person = session.get(Person.class, 3);

detached — объект был создан, но не управляется (или больше не управляется) JPA.
Объект, который до этого был привязан к контексту персистентности, но теперь отделен от него. По факту становится обычным Java объектом.

Отделение могло произойти по двум причинам:
- контекст персистентности был закрыт (закончилась транзакция, закрылась сессия),
- объект был явно отделен методом detach или clear от ещё существующего контекста.
- можно присоединить обратно к Persistent Context вызвав метод merge();

Подобный объект имеет заполненное поле id, сгенерированное базой, но контекст персистентности больше не следит за изменением этого объекта.
Похоже на состояние Transient

removed — объект создан, управляется JPA, но будет удален после commit’a транзакции.
Объект, ассоциированная с которым запись была удалена из базы. Такой объект так же не отслеживается контекстом персистентности и информация о нем больше не хранится в кэше первого уровня.

46
Q

Как влияет операция persist на Entity объекты каждого из четырех статусов?

A

persist()
new → managed, объект будет сохранен в базу при commit-е транзакции или в результате flush-операции.

managed → операция игнорируется, однако связанные entity могут поменять статус на managed, если у них есть аннотации каскадных изменений.

removed → managed.

detached → exception сразу или на этапе commit-а транзакции (так как у detached уже есть первичный ключ).

  1. New (новый) - если объект был создан, но еще не был сохранен в базу данных, метод persist() сохраняет его в базу данных и переводит его в состояние Managed (управляемый).
  2. Managed (управляемый) - если объект уже находится в состоянии Managed, метод persist() не делает никаких изменений.
  3. Detached (отсоединенный) - если объект был отсоединен от контекста персистентности, метод persist() снова связывает его с контекстом персистентности и переводит его в состояние Managed.
  4. Removed (удаленный) - если объект был удален из базы данных, метод persist() выбрасывает исключение EntityExistsException.
47
Q

Как влияет операция remove на Entity объекты каждого из четырех статусов?

A

new → операция игнорируется, однако зависимые Entity могут поменять статус на removed, если у них есть аннотации каскадных изменений и они имели статус managed;
managed → removed, запись объекта в базе данных будет удалена при commit-е транзакции (также произойдут операции remove для всех каскаднозависимых объектов);
detached → exception сразу или на этапе commit-а транзакции;
removed → операция игнорируется.

  1. New (новый) - если объект был создан, но еще не был сохранен в базу данных, операция remove() не делает никаких изменений.
  2. Managed (управляемый) - если объект уже находится в состоянии Managed, операция remove() удаляет его из базы данных и переводит его в состояние Removed (удаленный).
  3. Detached (отсоединенный) - если объект был отсоединен от контекста персистентности, операция remove() не делает никаких изменений.
  4. Removed (удаленный) - если объект уже был удален из базы данных, операция remove() не делает никаких изменений.
48
Q

Как влияет операция merge на объекты Entity каждого статуса?

A

merge()
new → будет создана новая managed entity, в которую будут скопированы данные объекта.

managed → операция игнорируется, однако операция merge сработает на каскадно зависимых entity, если их статус не managed.

removed → exception сразу или на этапе commit-а транзакции.

detached → либо данные будут скопированы в существующую БД managed entity с тем же первичным ключом, либо создана новая managed entity, в которую скопируются данные.

Операция merge() также влияет на объекты с каждым из четырех статусов следующим образом:

  1. New (новый) - если объект был создан, но еще не был сохранен в базу данных, операция merge() создает новую запись в базе данных и переводит объект в состояние Managed.
  2. Managed (управляемый) - если объект уже находится в состоянии Managed, операция merge() обновляет его значения в базе данных и оставляет его в состоянии Managed.
  3. Detached (отсоединенный) - если объект был отсоединен от контекста персистентности, операция merge() создает новую запись в базе данных и возвращает управляемый экземпляр.
  4. Removed (удаленный) - если объект уже был удален из базы данных, операция merge() создает новую запись в базе данных и переводит объект в состояние Managed.
49
Q

Как влияет операция refresh на объекты Entity каждого статуса?

A

Метод refresh() в Hibernate используется для обновления состояния объекта из базы данных.

Когда мы загружаем объект из базы данных в Hibernate, он сохраняет его состояние в кэше первого уровня, который находится в пределах одной сессии. Если в базе данных происходят изменения объекта извне (например, другой пользователь обновил его состояние), то состояние объекта в кэше первого уровня может быть неактуальным.

Чтобы обновить состояние объекта в кэше первого уровня из базы данных, мы можем вызвать метод refresh(). Hibernate загрузит текущее состояние объекта из базы данных и обновит его состояние в кэше первого уровня.

Также, метод refresh() может быть полезен в случае, когда мы хотим откатить изменения объекта, которые были внесены в рамках текущей транзакции. Вызов метода refresh() в этом случае обновит состояние объекта в кэше первого уровня до состояния, которое было сохранено в базе данных до начала текущей транзакции.

refresh()
managed → будут восстановлены все изменения из базы данных данного entity, также произойдет refresh всех каскадно зависимых объектов.

new, removed, detached → exception.

Операция refresh в контексте работы с объектами Entity обычно используется для получения актуальных данных из базы данных. При вызове метода refresh() объект Entity, который уже был загружен в память приложения и изменен, будет перезагружен из базы данных и обновлен данными из базы.

  1. New (новый) - если объект был создан, но еще не был сохранен в базу данных, операция refresh() не имеет эффекта.
  2. Managed (управляемый) - если объект уже находится в состоянии Managed, операция refresh() обновляет его значения из базы данных.
  3. Detached (отсоединенный) - если объект был отсоединен от контекста персистентности, операция refresh() не имеет эффекта.
  4. Removed (удаленный) - если объект уже был удален из базы данных, операция refresh() не имеет эффекта.
50
Q

Как влияет операция detach на объекты Entity каждого статуса?

A

new, detached → операция игнорируется.

Операция detach() отсоединяет объект Entity от контекста персистентности, что означает, что объект больше не будет отслеживаться менеджером сущностей и не будет автоматически сохраняться в базу данных при вызове операции flush(). В зависимости от статуса объекта Entity, влияние операции detach() может быть разным:

  1. New (новый) - если объект был создан, но еще не был сохранен в базу данных, операция detach() не имеет эффекта.
  2. Managed (управляемый) - если объект уже находится в состоянии Managed, операция detach() отсоединяет его от контекста персистентности.
  3. Detached (отсоединенный) - если объект уже был отсоединен от контекста персистентности, операция detach() не имеет эффекта.
  4. Removed (удаленный) - если объект уже был удален из базы данных, операция detach() не имеет эффекта.
51
Q

Разделение типов Hibernate

A

В широком смысле Hibernate разделяет типы на две группы:

  1. Типы значений (Value types).
  2. Типы сущностей (Entity types).

Типы сущностей

Сущности из-за своего уникального идентификатора существуют независимо от других объектов, тогда как типы значений нет. Экземпляры сущностей соответствуют строкам в таблице базы данных и различаются между собой благодаря уникальным идентификаторам.

Например, две сущности могут иметь абсолютно одинаковые значения полей, но имея разные идентификаторы (первичные ключи) они будут считаться разными, в отличие от POJO, которые при наличии абсолютно одинаковых значений полей будут считаться равными (equals вернет true). Из-за требования к наличию уникального идентификатора, сущности существуют независимо и определяют свой собственный жизненный цикл.

Типы значений

Это данные, которые не определяют свой собственный жизненный цикл. По сути, они принадлежат сущности (entity), которая определяет их жизненный цикл. С другой стороны, всё состояние объекта полностью состоит из типов значений. В свою очередь, типы значений подразделяются на три подкатегории:

  1. Базовые типы (Basic types).
  2. Встраиваемые типы (Embeddable types).
  3. Типы коллекций (Collection types).

Базовый тип значений

Соответствует одному столбцу в БД. Hibernate предоставляет ряд встроенных базовых типов, которые соответствуют естественным отображениям, рекомендованным спецификациями JDBC. Аннотация @Basic может быть применена к полю любого из следующих типов:

  1. Примитивы и их обертки.
  2. java.lang.String
  3. java.math.BigInteger
  4. java.math.BigDecimal
  5. java.util.Date
  6. java.util.Calendar
  7. java.sql.Date
  8. java.sql.Time
  9. java.sql.Timestamp
  10. byte[] or Byte[]
  11. char[] or Character[]
  12. enums
  13. любые другие типы, которые реализуют Serializable.
52
Q

Для чего нужна аннотация @Transactional?

A

Аннотация @Transactional - это аннотация, которая указывает, что метод должен быть выполнен в рамках транзакции. Эта аннотация может быть использована в Hibernate (и других ORM-фреймворках) для управления транзакциями базы данных.

Когда метод, помеченный аннотацией @Transactional, вызывается, Hibernate автоматически создает новую транзакцию базы данных и связывает ее с текущим потоком выполнения. Если метод выполняется успешно (без исключений), транзакция автоматически фиксируется и все изменения, внесенные в базу данных в рамках транзакции, сохраняются. Если же метод вызывает исключение, то транзакция откатывается и все изменения, внесенные в базу данных в рамках транзакции, отменяются.

Аннотация @Transactional может быть применена к методам или классам. Если аннотация применяется к методу, то только этот метод будет выполнен в рамках транзакции. Если аннотация применяется к классу, то все методы этого класса будут выполнены в рамках транзакции.

Важно отметить, что использование аннотации @Transactional не заменяет необходимость управления транзакциями в вашем коде. В некоторых случаях, например, когда несколько методов должны быть выполнены в рамках одной транзакции, вы можете использовать объект Transaction явным образом для управления транзакциями.

53
Q

Для чего нужна аннотация @Basic?

A

С помощью аннотации @Basic можно задать тип свойства сущности и свойства соответствующего столбца в таблице базы данных, а также другие атрибуты, такие как имя столбца, длину поля, может ли свойство быть null и т.д.

Аннотация Basic в Hibernate/JPA используется для маппинга базовых (простых) типов данных на столбцы в базе данных. Она применяется к полям или геттерам/сеттерам в классе сущности, которые представляют базовые типы данных, такие как целочисленные типы, строки, даты и другие.

@Basic указывает на простейший тип маппинга данных на колонку таблицы базы данных. Может быть применена к полю любого из следующих типов:
* примитивы и их обертки;
* java.lang.String;
* java.math.BigInteger;
* java.math.BigDecimal;
* java.util.Date;
* java.util.Calendar;
* java.sql.Date;
* java.sql.Time;
* java.sql.Timestamp;
* byte[] or Byte[];
* char[] or Character[];
* enums;
* любые другие типы, которые реализуют Serializable.

Аннотация @Basic определяет 2 атрибута:
1. optional – boolean (по умолчанию true) – определяет, может ли значение поля или свойства быть null. Игнорируется для примитивных типов. Но если тип поля не примитивного типа, то при попытке сохранения сущности будет выброшено исключение.
2. fetch – FetchType (по умолчанию EAGER) – определяет, должен ли этот атрибут извлекаться незамедлительно (EAGER) или лениво (LAZY). Это необязательное требование JPA, и провайдерам разрешено незамедлительно загружать данные, даже для которых установлена ленивая загрузка.

Аннотацию @Basic можно не ставить, как это и происходит по умолчанию.
Без аннотации @Basic при получении сущности из БД по умолчанию ее поля базового типа загружаются принудительно (EAGER) и значения этих полей могут быть null.

В целом @Basic надо использовать, если мы хотим изменить дефолтное поведение optional & fetch атрибутов

Например, если необходимо задать имя столбца в базе данных, отличное от имени поля или геттера/сеттера, то можно использовать параметр name:
~~~
@Basic(name = “EMP_NAME”)
private String name;
~~~
А если требуется задать тип данных в базе данных, отличный от автоматически определенного на основе Java-типа, то можно использовать параметр optional:
~~~
@Basic(optional = false)
private Integer age;
~~~

Одним из параметров этой аннотации является fetch type, который определяет, как будет производиться загрузка данных из базы данных в память.
~~~
@Basic(fetch = FetchType.LAZY)
private String name;
~~~

54
Q

Для чего нужна аннотация @Column?

A

Аннотация @Column используется в JPA/Hibernate для маппинга атрибутов сущности на столбцы в базе данных. Она позволяет настраивать различные атрибуты столбца, такие как имя, тип данных, ограничения и т.д.
Cопоставляет поле класса столбцу таблицы, а её атрибуты определяют поведение в этом столбце, используется для генерации схемы базы данных.

Некоторые из параметров, которые можно настроить с помощью аннотации @Column:

  • name: имя столбца в таблице базы данных, к которому относится атрибут.
  • nullable: указывает, может ли столбец содержать null-значение.
  • unique: указывает, должно ли значение в столбце быть уникальным.
  • length: максимальная длина значения столбца.
  • precision: общее количество значащих цифр в числовых столбцах, которые используют числа с плавающей точкой или числа с фиксированной точностью.
  • scale: количество десятичных знаков в числовых столбцах, которые используют числа с плавающей точкой или числа с фиксированной точностью.
  • insertable: указывает, можно ли вставлять значения в столбец при выполнении операции persist.
  • updatable: указывает, можно ли обновлять значения столбца при выполнении операции merge.
  • columnDefinition: определяет тип данных столбца с использованием SQL-синтаксиса, который принимается в качестве аргумента.
  • table: имя таблицы, которой принадлежит столбец.

@Basic vs @Column:
Коротко, в @Column задаем имя и constraints, а в @Basic – FetchTypes.

Атрибуты @Basic применяются к сущностям JPA, тогда как атрибуты @Column применяются к столбцам базы данных.
@Basic имеет атрибут optional, который говорит о том, может ли поле объекта быть null или нет; с другой стороны атрибут nullable аннотации @Column указывает, может ли соответствующий столбец в таблице быть null.
Мы можем использовать @Basic, чтобы указать, что поле должно быть загружено лениво.
Аннотация @Column позволяет нам указать имя столбца в таблице и ряд других свойств: a. insertable/updatable - можно ли добавлять/изменять данные в колонке, по умолчанию true; b. length - длина, для строковых типов данных, по умолчанию 255.

55
Q

Для чего нужна аннотация @Access?

A

Она определяет как JPA будет обращаться к атрибутам entity: как к полям класса (FIELD) или как к свойствам класса (PROPERTY), имеющие гетеры (getter) и сетеры (setter).

Совокупность полей и методов (свойств) сущности называется атрибутами.

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

Если же использовать AccessType.FIELD, то логика, связанная с геттерами и сеттерами, будет игнорироваться, поскольку Hibernate получает доступ к полям напрямую. Однако это может быть полезно в случае, когда класс, определенный с помощью аннотаций, является недоступным для изменения, и в нем нет геттеров и сеттеров для полей.

Для чтения и записи полей Hibernate использует два подхода:

Field access (доступ по полям).
Hibernate напрямую работает с полями сущности, читая и записывая их.

Property access (доступ по свойствам).
Hibernate использует геттеры и сеттеры для чтения и записи полей сущности. Но есть требование - у сущности с property access названия методов должны соответствовать требованиям JavaBeans. Например, если у сущности Customer есть поле с именем firstName, то у этой сущности должны быть определены методы getFirstName и setFirstName для чтения и записи поля firstName.

 @Access(AccessType.PROPERTY)
    @Column(name = "full_name")
    private String fullName;
-----------------------------------------------
 @Access(AccessType.FIELD)
    @Column(name = "first_name")
    private String firstName;

Аннотация @Access в Hibernate используется для указания стратегии доступа к полям сущности. Она определяет, как будут получаться и устанавливаться значения полей объекта при работе с базой данных.

Существует две стратегии доступа:

  • Property Access (доступ через свойства) - значения полей объекта получаются и устанавливаются через соответствующие методы-геттеры и методы-сеттеры. Эту стратегию можно задать с помощью аннотации @Access(AccessType.PROPERTY).
  • Field Access (доступ через поля) - значения полей объекта получаются и устанавливаются напрямую через соответствующие поля объекта. Эту стратегию можно задать с помощью аннотации @Access(AccessType.FIELD).
    Стратегия доступа по умолчанию - Property Access. Она работает так: при сохранении объекта Hibernate использует метод-сеттер для получения значения поля, а при чтении объекта из базы данных Hibernate использует метод-геттер для установки значения поля.

Таким образом, использование аннотации @Access позволяет точно определить, каким образом Hibernate будет получать и устанавливать значения полей объекта при работе с базой данных.

56
Q

Для чего нужна аннотация @Cacheable?

A

Аннотация @Cacheable в Hibernate используется для настройки кэширования запросов второго уровня.
Если сущность помечена этой аннотацией, то Hibernate будет кэшировать результаты запросов, связанных с этой сущностью, что может ускорить работу приложения. Кэширование может производиться как внутри JVM, так и во внешней системе кэширования (например, Redis или Memcached).

Аннотация @Cacheable размещается над классом сущности. Её действие распространяется на эту сущность и её наследников, если они не определили другое поведение.

Используется для указания того, должна ли сущность храниться в кэше второго уровня, в случае, если в файле persistence.xml (или в свойстве javax.persistence.sharedCache.mode конфигурационного файла) для элемента shared-cache-mode установлено одно из значений:

  • ENABLE_SELECTIVE: только сущности с аннотацией @Cacheable (равносильно значению по умолчанию @Cacheable(value=true)) будут сохраняться в кэше второго уровня.
  • DISABLE_SELECTIVE: все сущности будут сохраняться в кэше второго уровня, за исключением сущностей, помеченных аннотацией @Cacheable(value=false)как некэшируемые.
  • ALL: сущности всегда кэшируются, даже если они помечены как некэшируемые.
  • NONE: ни одна сущность не кэшируется, даже если помечена как кэшируемая. При данной опции имеет смысл вообще отключить кэш второго уровня.
  • UNSPECIFIED: применяются значения по умолчанию для кэша второго уровня, определенные Hibernate. Это эквивалентно тому, что вообще не используется shared-cache-mode, так как Hibernate не включает кэш второго уровня, если используется режим UNSPECIFIED.

Опции, которые можно использовать с аннотацией @Cacheable:
* value - указывает на конкретный кэш-регион, в котором будет храниться сущность. Если не указан, используется регион по умолчанию.
* key - определяет выражение, которое будет использоваться для формирования ключа кэша. Если не указан, используется идентификатор сущности.
* shared - указывает, может ли сущность использоваться другими кэш-провайдерами или нет. По умолчанию установлено значение true.
* cacheManager - указывает на конкретный кэш-менеджер, который будет использоваться для управления кэшем. Если не указан, используется менеджер по умолчанию.
* cacheResolver - указывает на конкретный резолвер кэша, который будет использоваться для определения кэш-региона. Если не указан, используется резолвер по умолчанию.

57
Q

Для чего нужны аннотации @Embedded и @Embeddable?

A

@Embeddable – аннотация JPA, размещается над классом для указания того, что класс является встраиваемым в другие классы.
@Embeddable указывает, что класс является объектом-значением

@Embedded – аннотация JPA, используется для размещения над полем в классе-сущности для указания того, что внедряется встраиваемый класс.
@Embedded указывает на то, что этот объект-значение встраивается в другой объект (обычно сущность). При этом, все поля аннотированного класса @Embeddable автоматически становятся полями класса-владельца.

@Embeddable
public class Address {
    private String street;
    private String city;
    private String zipCode;
    // геттеры и сеттеры
}
@Entity
public class Person {
    @Id
    private Long id;
    private String name;
    @Embedded
    private Address address;
    // геттеры и сеттеры
}

В этом примере класс Address является объектом-значением, а класс Person содержит его в качестве встраиваемого объекта (embedded object). При сохранении объекта Person Hibernate автоматически сохранит и объект Address.

Использование аннотаций @Embedded и @Embeddable упрощает работу с объектами-значениями, так как не требует отдельного сохранения каждого атрибута в базу данных, а позволяет сохранять и загружать их вместе с объектом-владельцем.

58
Q

Как смапить составной ключ?@IdClass

A

Составной первичный ключ, также называемый составным ключом, представляет собой комбинацию из двух или более столбцов для формирования первичного ключа таблицы.

В JPA есть требования к составному ключу:

  • составной ключ должен быть представлен классом первичного ключа, при этом используется одна из двух аннотаций: @IdClass и @EmbeddedId;
  • класс первичного ключа должен быть публичным и иметь публичный конструктор без аргументов;
  • класс первичного ключа должен имплементировать маркерный интерфейс Serializable;
  • класс первичного ключа должен иметь методы equals и hashCode;
  • атрибуты, представляющие поля составного ключа, могут быть базовыми, составными и @ManyToOne, но не могут быть коллекциями или @OneToOne.

Для маппинга составного ключа с использованием аннотации @IdClass необходимо выполнить следующие шаги:

Создать класс, который будет представлять составной ключ. Этот класс должен содержать поля, соответствующие полям составного ключа, а также переопределить методы equals() и hashCode().

public class CompositeKey implements Serializable {
    private String field1;
    private int field2;
    public CompositeKey() {
    }
    public CompositeKey(String field1, int field2) {
        this.field1 = field1;
        this.field2 = field2;
    }

В классе, который представляет сущность, использовать аннотацию @IdClass, указав в качестве значения класс, который был создан на первом шаге. Аннотировать поля, которые являются частью составного ключа, аннотацией @Id.

@Entity
@IdClass(CompositeKey.class)
public class MyEntity {
    @Id
    private String field1;

    @Id
    private int field2;

    // остальные поля и методы
}

Использовать объект составного ключа при выполнении операций с сущностью.
~~~
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();

CompositeKey key = new CompositeKey(“value1”, 2);
MyEntity entity = em.find(MyEntity.class, key);

em.getTransaction().commit();
em.close();
~~~

59
Q

Как смапить составной ключ?@EmbeddedId

A

Создать класс, который будет представлять составной ключ. В этом классе нужно объявить приватные поля, которые будут представлять части составного ключа.
В этом случае класс первичного ключа, OrderItemId, должен быть аннотирован @Embeddable:
~~~
@Embeddable
public class OrderItemId implements Serializable {
private Long orderId;
private Long productId;

public OrderItemId() {}

public OrderItemId(Long orderId, Long productId) {
    this.orderId = orderId;
    this.productId = productId;
}

// getters, setters, equals, hashCode } ~~~ В классе, который будет являться сущностью, которая использует составной ключ, нужно объявить приватное поле с типом созданного в предыдущем шаге класса. ~~~ @Entity @Table(name = "orders_items") public class OrderItem {
@EmbeddedId
private OrderItemId id;

private int quantity;

// getters, setters } ~~~ В классе OrderItem необходимо использовать поля составного ключа в качестве полей-членов класса. Также нужно объявить геттеры и сеттеры для этих полей.
@Entity
@Table(name = "orders_items")
public class OrderItem {
    @EmbeddedId
    private OrderItemId id;
    private int quantity;
    public Long getOrderId() {
        return id.getOrderId();
    }
    public void setOrderId(Long orderId) {
        id.setOrderId(orderId);
    }
    public Long getProductId() {
        return id.getProductId();
    }
    public void setProductId(Long productId) {
        id.setProductId(productId);
    }
    public int getQuantity() {
        return quantity;
    }
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

В классе OrderItemId нужно добавить аннотации @Column для каждого поля-члена, чтобы указать их имена в базе данных.
~~~
@Embeddable
public class OrderItemId implements Serializable {
@Column(name = “order_id”)
private Long orderId;

@Column(name = "product_id")
private Long productId;

public OrderItemId() {}

public OrderItemId(Long orderId, Long productId) {
    this.orderId = orderId;
    this.productId = productId;
}
// getters, setters, equals, hashCode } ~~~ Теперь Hibernate будет использовать составной ключ, который представлен классом OrderItemId, для уникальной идентификации каждой записи в таблице базы данных.
60
Q

@IdClass vs @EmbeddedId

A

с @IdClass нам пришлось указывать столбцы дважды - в AccountId и в Account. Но с @EmbeddedId мы этого не сделали;
JPQL-запросы с @IdClass проще. С @EmbeddedId, чтобы получить доступ к полю, нам нужно из сущности обратиться к встраиваемому классу и потом к его полю:
SELECT account.accountNumber FROM Account account // с @IdClass

  SELECT book.bookId.title FROM Book book // с @EmbeddedId

@EmbeddedId более подробна, чем @IdClass, поскольку мы можем получить доступ ко всему объекту первичного ключа, используя метод доступа к полю в классе-сущности. Это также дает четкое представление о полях, которые являются частью составного ключа, поскольку все они агрегированы в классе, который доступен только через метод доступа к полям;
@IdClass может быть предпочтительным выбором по сравнению с @EmbeddedId в ситуациях, когда класс составного первичного ключа поступает из другого модуля или устаревшего кода, а также когда мы не можем его изменить, например, чтобы установить аннотацию @EmbeddedId. Для таких сценариев, где мы не можем изменить класс составного ключа, аннотация @IdClass является единственным выходом;
если мы собираемся получить доступ к частям составного ключа по отдельности, мы можем использовать @IdClass, но в тех местах, где мы часто используем полный идентификатор в качестве объекта, @EmbeddedId предпочтительнее.

61
Q

Для чего нужна аннотация @ID? Какие GeneratedValue вы знаете?

A

@Id
Аннотация @Id определяет простой (не составной) первичный ключ, состоящий из одного поля. В соответствии с JPA, допустимые типы атрибутов для первичного ключа:

  1. примитивные типы и их обертки;
  2. строки;
  3. BigDecimal и BigInteger;
  4. java.util.Date и java.sql.Date.

Если хотим, чтобы значение первичного ключа генерировалось автоматически, необходимо добавить первичному ключу аннотацию @GeneratedValue.
Согласно спецификации JPA возможно 4 различных варианта: AUTO, IDENTITY, SEQUENCE, TABLE. Если мы не укажем значение явно, типом генерации по умолчанию будет AUTO. Спецификация JPA строго не определяет поведение этих стратегий.

  1. AUTO (default). Указывает, что Hibernate должен выбрать подходящую стратегию для конкретной базы данных, учитывая ее диалект, так как у разных БД разные способы по умолчанию. Поведение по умолчанию – исходить из типа поля идентификатора.
    Например, для MySQL может использоваться AUTO_INCREMENT.
  2. IDENTITY.При использовании стратегии IDENTITY, значение первичного ключа генерируется автоматически при вставке записи в таблицу. База данных генерирует новое значение ID для каждой вставленной записи. Обычно это реализуется с помощью автоинкрементируемых полей в таблице.
    В промежутках транзакций сущность будет сохранена.
  3. SEQUENCE. Тип генерации, рекомендуемый документацией Hibernate. Для получения значений первичного ключа Hibernate должен использовать имеющиеся в базе данных механизмы генерации последовательных значений (Sequence).
    При использовании этой стратегии Hibernate создает отдельную таблицу в базе данных для хранения текущего значения генератора. Значения генерируются путем увеличения текущего значения на шаг (increment) и сохранения его в таблице.
    При сохранении новой сущности Hibernate выбирает следующее значение из генератора и присваивает его идентификатору сущности.
    Но если БД не поддерживает тип SEQUENCE, то Hibernate автоматически переключится на тип TABLE. В промежутках транзакций сущность не будет сохранена, так как Hibernate возьмет из таблицы id hibernate-sequence и вернется обратно в приложение.

SEQUENCE – это объект базы данных, который генерирует инкрементные
целые числа при каждом последующем запросе.

Пример использования аннотации @GeneratedValue с параметром strategy=GenerationType.SEQUENCE:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_sequence")
@SequenceGenerator(name="my_sequence", sequenceName = "my_sequence")
private Long id;

В данном примере мы используем стратегию SEQUENCE с генератором my_sequence и именем последовательности my_sequence. При сохранении новых сущностей Hibernate будет использовать этот генератор для генерации идентификаторов.

  1. TABLE.Hibernate должен получать первичные ключи для сущностей из создаваемой для этих целей таблицы, способной содержать именованные сегменты значений для любого количества сущностей. Требует использования пессимистических блокировок, которые помещают все транзакции в последовательный порядок и замедляет работу приложения.

При использовании этой стратегии, JPA создает отдельную таблицу в базе данных, которая используется для хранения информации об идентификаторах. Эта таблица состоит из трех столбцов: “table_name”, “sequence_name” и “value”.

Алгоритм работы этой стратегии следующий:

При вставке новой сущности в базу данных, JPA генерирует SQL-запрос для получения следующего доступного идентификатора из таблицы идентификаторов.
Этот идентификатор присваивается новой сущности и сохраняется в базе данных вместе с остальными полями сущности.
Пример использования стратегии TABLE:
~~~
@Entity
@TableGenerator(name=”table_gen”, table=”id_gen”,
pkColumnName=”gen_name”, valueColumnName=”gen_value”)
public class MyEntity {
@Id
@GeneratedValue(strategy=GenerationType.TABLE, generator=”table_gen”)
private Long id;

}
~~~
Здесь мы определяем таблицу с именем “id_gen”, которая будет использоваться для хранения идентификаторов сущностей. В этой таблице есть два столбца: “gen_name” и “gen_value”. Затем мы используем аннотацию @TableGenerator для настройки этой таблицы и связываем ее с аннотацией @GeneratedValue, указывая стратегию генерации “TABLE” и имя таблицы генератора.

62
Q

Расскажите про аннотации @JoinColumn и @JoinTable, @JoinColumns? Где и для чего они используются?

A

Аннотации @JoinColumn, @JoinTable и @JoinColumns являются частью Java Persistence API (JPA) и используются для определения отношений между сущностями в базе данных.

@JoinColumn используется для указания столбца FOREIGN KEY, используемого при установлении связей между сущностями.
Можно указать @JoinColumn как во владеющей таблице, так и во владеемой, но столбец с внешними ключами все равно появится во владеющей таблице.

@JoinColumn используется для указания связи между двумя таблицами через внешний ключ. Она применяется к полю, которое является внешним ключом, и указывает на имя столбца в таблице, который используется в качестве внешнего ключа. Например:

@Entity
public class Order {
    @Id
    private Long id;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // other fields and methods
}

Особенности использования:
* @OneToOne: означает, что появится столбец в таблице сущности-владельца связи, который будет содержать внешний ключ, ссылающийся на первичный ключ владеемой сущности;
* @OneToMany/@ManyToOne: если не указать на владеемой стороне связи @mappedBy, создается joinTable с ключами обеих таблиц. Но при этом же у владельца создается столбец с внешними ключами.

@JoinColumns используется для группировки нескольких аннотаций @JoinColumn, которые используются при установлении связей между сущностями или коллекциями, у которых составной первичный ключ и требуется несколько колонок для указания внешнего ключа.
В каждой аннотации @JoinColumn должны быть указаны элементы name и
referencedColumnName.
Когда у вас есть связь между двумя таблицами, обычно один столбец используется в качестве внешнего ключа, чтобы связать две таблицы. Но в некоторых случаях внешний ключ может состоять из нескольких столбцов, которые вместе образуют уникальный идентификатор.

Например, предположим, что у вас есть таблица сотрудников и таблица отделов, и каждый отдел имеет уникальное имя и расположение. Чтобы связать сущность Employee с сущностью Department, необходимо использовать два столбца, один для имени отдела и один для расположения отдела, чтобы уникально идентифицировать каждый отдел.

В этом случае вы можете использовать аннотацию @JoinColumns, чтобы указать имена столбцов, которые будут использоваться для связи между сущностями:

@Entity
public class Employee {
    @Id
    private Long id;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name="dept_name", referencedColumnName="name"),
        @JoinColumn(name="dept_location", referencedColumnName="location")
    })
    private Department department;

    // other fields and methods
}

В этом примере мы используем аннотацию @JoinColumns вместе с @JoinColumn для указания двух столбцов, dept_name и dept_location, которые будут использоваться для связи между таблицей employees и таблицей departments.

Обратите внимание, что referencedColumnName используется для указания имени столбца, на который ссылается внешний ключ в таблице departments.

@JoinTable
Аннотация @JoinTable используется для указания связывающей (сводной, третьей) таблицы между двумя другими таблицами.
@JoinTable используется для определения отношений многие-ко-многим между двумя сущностями. Она применяется к полю, которое представляет коллекцию других сущностей, и указывает на таблицу, которая используется для хранения связи между этими сущностями. Например:
~~~
@Entity
public class Student {
@Id
private Long id;
@ManyToMany
@JoinTable(
name = “enrollments”,
joinColumns = @JoinColumn(name = “student_id”),
inverseJoinColumns = @JoinColumn(name = “course_id”)
)
private List<Course> courses;
// other fields and methods
}
~~~
В этом примере сущность Student связана с сущностью Course через коллекцию courses. @JoinTable указывает на таблицу enrollments, которая используется для хранения связей между этими сущностями. joinColumns и inverseJoinColumns указывают на имена столбцов, которые представляют внешние ключи на таблицы students и courses соответственно.</Course>

63
Q

Для чего нужны аннотации @OrderBy и @OrderColumn, чем они отличаются?

A

Аннотации @OrderBy и @OrderColumn используются в JPA (Java Persistence API) для указания порядка сортировки результатов запроса.

@OrderBy позволяет указать порядок сортировки для элементов коллекции, которая связана с сущностью. Например:

@OneToMany(mappedBy = "parent")
@OrderBy("name ASC")
private List<Child> children;

В этом примере мы указываем, что коллекция children должна быть отсортирована по имени (name) в порядке возрастания (ASC).

@OrderColumn используется для управления порядком сортировки элементов в коллекции, которая является отображением таблицы базы данных. Например:

@OneToMany(mappedBy = "parent")
@OrderColumn(name = "position")
private List<Child> children;

В этом примере мы указываем, что коллекция children должна быть отсортирована по значению столбца position таблицы базы данных.

@OrderBy vs @OrderColumn Порядок, указанный в @OrderBy, применяется только в рантайме при выполнении запроса к БД, то есть в контексте персистентности,
в то время как при использовании @OrderColumn, порядок сохраняется в отдельном столбце таблицы и поддерживается при каждой вставке/обновлении/удалении элементов.

@OrderBy используется для управления порядком сортировки элементов коллекции в памяти приложения. Это может быть полезно, если вам нужно получить отсортированный список элементов, но порядок не важен в базе данных. Например, если у вас есть сущность User, у которой есть список ролей (List<Role>), и вы хотите получить отсортированный список ролей пользователя по названию роли, вы можете использовать @OrderBy("name ASC").</Role>

@OrderColumn используется для управления порядком сортировки элементов в базе данных. Это может быть полезно, если вам нужно сохранить порядок элементов в базе данных, например, если вы хотите сохранить порядок дочерних элементов в родительской сущности. Например, если у вас есть сущность Category, у которой есть список товаров (List<Product>), и вы хотите сохранить порядок товаров в базе данных, вы можете использовать @OrderColumn(name = "position").</Product>

Кроме того, следует учитывать производительность. Использование @OrderBy может быть производительнее, чем использование @OrderColumn, поскольку сортировка выполняется в памяти приложения. Однако, при большом количестве элементов сортировка в памяти может привести к проблемам производительности, и тогда @OrderColumn будет предпочтительнее, так как порядок элементов будет сохранен в базе данных и сортировка будет выполняться на уровне базы данных.

64
Q
  1. Для чего нужна аннотация Transient?
A

Аннотация @Transient в Hibernate используется для пометки поля сущности, которое не должно быть сохранено в базу данных.

При сохранении сущности Hibernate автоматически сохраняет все ее поля в базу данных, если они соответствуют соответствующим столбцам в таблице базы данных. Если вы хотите исключить определенное поле из сохраняемых полей, вы можете пометить его аннотацией @Transient. Это может быть полезно, если, например, вы хотите добавить поле к сущности только для использования внутри приложения, но не хотите, чтобы оно было сохранено в базе данных.

@Entity
public class Person {
    
    @Id
    private Long id;
    
    private String name;
    
    private int age;
    
    @Transient
    private String tempData;
    
    // getters and setters
}

В этом примере мы пометили поле tempData аннотацией @Transient, чтобы оно не сохранялось в базу данных вместе с остальными полями сущности Person.

Таким образом, аннотация @Transient позволяет исключить поле из сохранения в базу данных при сохранении сущности в Hibernate.

65
Q

Какие шесть видов блокировок (lock) описаны в спецификации JPA (или какие есть значения у enum LockModeType в JPA)?

A

READ: Это значение используется для чтения записей с блокировкой на чтение. Транзакция, которая получает блокировку на чтение, может прочитать запись, но не может её изменить.

WRITE: Это значение используется для получения блокировки на запись. Транзакция, которая получает блокировку на запись, может изменять запись.

OPTIMISTIC: Это значение используется для оптимистической блокировки. Оптимистическая блокировка позволяет нескольким транзакциям работать с одной записью, но конфликты изменений должны быть обнаружены при попытке сохранения изменений в базу данных.

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

PESSIMISTIC_READ: Это значение используется для получения пессимистической блокировки на чтение. Транзакция, которая получает пессимистическую блокировку на чтение, может прочитать запись и не даст другой транзакции получить блокировку на запись до тех пор, пока первая транзакция не завершится.

PESSIMISTIC_WRITE: Это значение используется для получения пессимистической блокировки на запись. Транзакция, которая получает пессимистическую блокировку на запись, может изменять запись и не даст другой транзакции получить блокировку на запись или чтение до тех пор, пока первая транзакция не завершится.

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

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

66
Q

Оптимистичное блокирование

A

Оптимистичное блокирование - подход предполагает, что параллельно выполняющиеся транзакции редко обращаются к одним и тем же данным и позволяет им свободно выполнять любые чтения и обновления данных. Но при окончании транзакции производится проверка, изменились ли данные в ходе выполнения данной транзакции и, если да, транзакция обрывается и выбрасывается OptimisticLockException.

Оптимистичное блокирование в JPA реализовано путём внедрения в сущность специального поля версии:

@Version

private long version;

Поле, аннотирование @Version, может быть целочисленным или временнЫм. При завершении транзакции, если сущность была заблокирована оптимистично, будет проверено, не изменилось ли значение @Version кем-либо ещё, после того как данные были прочитаны, и, если изменилось, будет выкинуто OptimisticLockException. Использование этого поля позволяет отказаться от блокировок на уровне базы данных и сделать всё на уровне JPA, улучшая уровень конкурентности.

Позволяет отказатьсь от блокировок на уровне БД и делать всё с JPA.

JPA поддерживает два типа оптимистичной блокировки:

LockModeType.OPTIMISTIC — блокировка на чтение, которая работает, как описано выше: если при завершении транзакции кто-то извне изменит поле @Version, то транзакция автоматически будет откачена и будет выброшено OptimisticLockException.
LockModeType.OPTIMISTIC_FORCE_INCREMENT — блокировка на запись. Ведёт себя как и блокировка на чтение, но при этом увеличивает значение поля @Version.

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

Примеры оптимистических блокировок в Hibernate:

В JPA аннотация @Version используется для оптимистических блокировок. В этом случае Hibernate будет использовать поле-версию (обычно типа long), которое будет автоматически инкрементироваться при каждом обновлении записи. При попытке обновить запись, Hibernate сравнит текущую версию с версией в базе данных. Если они не совпадают, то это означает, что запись была изменена другой транзакцией, и текущая транзакция будет откатана.

@Entity
public class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @Version
    private Long version;
    
    // getters and setters
}

Другой способ использования оптимистической блокировки - использование аннотации @OptimisticLocking. Это позволяет настроить тип оптимистической блокировки. Доступные значения - OptimisticLockType.ALL, OptimisticLockType.DIRTY и OptimisticLockType.VERSION.

@Entity
@org.hibernate.annotations.OptimisticLocking(type = OptimisticLockType.ALL)
public class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @Version
    private Long version;
    
    // getters and setters
}
67
Q

Пессимистичное блокирование

A

Пессимистичное блокирование - подход напротив, ориентирован на транзакции, которые часто конкурируют за одни и те же данные и поэтому блокирует доступ к данным в тот момент когда читает их.

Другие транзакции останавливаются, когда пытаются обратиться к заблокированным данным и ждут снятия блокировки (или кидают исключение). Пессимистичное блокирование выполняется на уровне базы и поэтому не требует вмешательств в код сущности.

Блокировки ставятся путём вызова метода lock() у EntityManager, в который передаётся сущность, требующая блокировки и уровень блокировки:

EntityManager em = entityManagerFactory.createEntityManager();

em.lock(company1, LockModeType.OPTIMISTIC);

LockModeType.PESSIMISTIC_READ — данные блокируются в момент чтения и это гарантирует, что никто в ходе выполнения транзакции не сможет их изменить. Остальные транзакции, тем не менее, смогут параллельно читать эти данные. Использование этой блокировки может вызывать долгое ожидание блокировки или даже выкидывание PessimisticLockException.
LockModeType.PESSIMISTIC_WRITE — данные блокируются в момент записи и никто с момента захвата блокировки не может в них писать и не может их читать до окончания транзакции, владеющей блокировкой. Использование этой блокировки может вызывать долгое ожидание блокировки.
Кроме того, для сущностей с полем, аннотированным @Version, существует третий вариант пессимистичной блокировки:

LockModeType.PESSIMISTIC_FORCE_INCREMENT — ведёт себя как LockModeType.PESSIMISTIC_WRITE, но в конце транзакции увеличивает значение поля @Version, даже если фактически сущность не изменилась.

С другой стороны, пессимистические блокировки применяются, когда конфликты доступа к данным представляют серьезную угрозу целостности данных. В этом случае, перед чтением или изменением данных, происходит установка блокировки на соответствующие ресурсы, чтобы другие транзакции не могли изменять данные до окончания текущей транзакции. Это может привести к блокированию таблицы и ухудшению производительности

Примеры пессимистических блокировок в Hibernate:

Использование метода lock() EntityManager-а для получения блокировки записи перед её изменением.

EntityManager em = ...;
em.getTransaction().begin();

Employee emp = em.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE);
emp.setName("New name");

em.getTransaction().commit();

Использование аннотации @Lock для получения блокировки записи перед чтением или изменением.
~~~
@Entity
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
@Version
private Long version;

// getters and setters

@Lock(LockModeType.PESSIMISTIC_WRITE)
public static Employee findById(Long id) {
    EntityManager em = ...;
    return em.find(Employee.class, id);
} } ~~~
68
Q

Основное отличие между оптимистическими и пессимистическими блокировками

A

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

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

С другой стороны, оптимистическая блокировка не блокирует запись на момент своего начала. Вместо этого, оптимистическая блокировка использует механизм версионирования, чтобы обнаруживать конфликты изменений. Когда транзакция получает доступ к записи, она сохраняет текущую версию записи. При попытке сохранить изменения в базу данных, транзакция проверяет, была ли версия записи изменена другой транзакцией после того, как она была прочитана. Если версия записи изменилась, транзакция может обнаружить конфликт и принять соответствующие меры, например, откатить свои изменения или повторно прочитать данные и попытаться сохранить изменения еще раз.

Таким образом, пессимистическая блокировка гарантирует монопольный доступ к заблокированным данным, тогда как оптимистическая блокировка позволяет нескольким транзакциям работать с одними и теми же данными, обнаруживая конфликты изменений при сохранении данных. Какой тип блокировки выбрать, зависит от требований конкретной задачи и ситуации.

69
Q

Какие два вида кэшей (cache) вы знаете в JPA и для чего они нужны?

A
  • first-level cache Кэш первого уровня (cache context) является частью контекста персистентности Session и хранит все сущности, которые были прочитаны или сохранены в рамках текущей сессии. Кэш первого уровня обеспечивает избежание повторных запросов к базе данных при повторном доступе к той же сущности в рамках текущей сессии. Это улучшает производительность и снижает нагрузку на базу данных.;

кэш 1 уровня это по факту мапа, где ключ это клас и айди, а значение это сущность, которую мы достали из БД по данному ключу.

сохраняет managed сущности на время транзакции

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

Также стоит отметить, что кэш первого уровня доступен только в рамках одной транзакции и не переживает коммита транзакции. Кэш первого уровня является простым и эффективным механизмом для избежания повторных запросов к базе данных в рамках одной транзакции.

  • second-level cache (кеш второго уровня) кэширует данные транзакций от одной фабрики сессий. Провайдер JPA может, но не обязан реализовывать работу с кешем второго уровня.
    Кэш второго уровня (cache provider) является общесистемным и может использоваться для кэширования сущностей и запросов между несколькими SessionFactory. Кэш второго уровня может существенно снизить количество запросов к базе данных и улучшить производительность в приложениях, где многие сессии работают с одним и тем же набором данных. В качестве провайдеров кэша второго уровня могут использоваться различные решения, такие как Ehcache, Hazelcast, Infinispan и др.

кэш второго уровня, это кэш на уровне SessionFactory. По умолчанию он выключен, и требует дополнительной настройки.
кэш разбит на регионы и в них хранятся сущности. это аналог персистенс контекста. Кэш нет смысла использовать для часто изменяемых сущностей.

  • кэш запросов - или кэш третьего уровня может использоваться для кэширования результатов выполнения запросов, чтобы ускорить работу приложения. Например, если приложение часто выполняет один и тот же запрос к базе данных, результаты этого запроса могут быть закэшированы в кэше третьего уровня для более быстрого доступа в будущем. при этом ключом является сам запрос и те параметры, которые были переданы в запрос.
70
Q

hit miss put в контексте кэша

A

В контексте кэша используются три основных термина: hit (попадание), miss (промах) и put (помещение). Рассмотрим каждый из них более подробно:

  • Hit (попадание) - это событие, когда запрошенная сущность находится в кэше и может быть быстро получена из кэша, без необходимости обращения к базе данных. Такой случай уменьшает время ожидания запроса и повышает производительность приложения.
  • Miss (промах) - это событие, когда запрошенная сущность отсутствует в кэше. В этом случае приложение должно выполнить запрос к базе данных для получения требуемой сущности. Промах может произойти в случае, если сущность была удалена из кэша из-за истечения времени жизни кэша или если это первый запрос на загрузку данной сущности.
  • Put (помещение) - это событие, когда сущность была загружена из базы данных и помещена в кэш. При этом, если в кэше уже была сохранена сущность с таким же идентификатором, то она будет заменена новой версией. Это происходит, например, при сохранении или обновлении сущности в базе данных.
71
Q

Кэш первого уровня

A

Кэш первого уровня – это кэш сессии (Session), который является обязательным, это и есть PersistenceContext. Через него проходят все запросы.

В том случае, если мы выполняем несколько обновлений объекта, Hibernate старается отсрочить (насколько это возможно) обновление этого объекта для того, чтобы сократить количество выполненных запросов в БД. Например, при пяти истребованиях одного и того же объекта из БД в рамках одного persistence context, запрос в БД будет выполнен один раз, а остальные четыре загрузки будут выполнены из кэша. Если мы закроем сессию, то все объекты, находящиеся в кэше, теряются, а далее – либо сохраняются в БД, либо обновляются.

Особенности кэша первого уровня:

❖ включен по умолчанию, его нельзя отключить;

❖ связан с сессией (контекстом персистентности), то есть разные сессии видят только объекты из своего кэша, и не видят объекты, находящиеся в кэшах других сессий;

❖ при закрытии сессии PersistenceContext очищается - кэшированные объекты, находившиеся в нем, удаляются;

❖ при первом запросе сущности из БД, она загружается в кэш, связанный с этой сессией;

❖ если в рамках этой же сессии мы снова запросим эту же сущность из БД, то она будет загружена из кэша, и никакого второго SQL-запроса в БД сделано не будет;

❖ сущность можно удалить из кэша сессии методом evict(), после чего следующая попытка получить эту же сущность повлечет обращение к базе данных;

❖ метод clear() очищает весь кэш сессии.

72
Q

Кэш второго уровня

A

Если кэш первого уровня привязан к объекту сессии, то кэш второго уровня привязан к объекту-фабрике сессий (Session Factory object) и, следовательно, кэш второго уровня доступен одновременно в нескольких сессиях или контекстах персистентности. Кэш второго уровня требует некоторой настройки и поэтому не включен по умолчанию. Настройка кэша заключается в конфигурировании реализации кэша и разрешения сущностям быть закэшированными.

Hibernate не реализует сам никакого in-memory сache, а использует существующие реализации кэшей.

73
Q

Как работать с кешем 2 уровня?

A

Чтение из кэша второго уровня происходит только в том случае, если нужный объект не был найден в кэше первого уровня.

Hibernate поставляется со встроенной поддержкой стандарта кэширования Java JCache, а также двух популярных библиотек кэширования: Ehcache и Infinispan.

Для работы с кешем второго уровня в JPA Hibernate необходимо выполнить несколько шагов:
1. Настройка провайдера кэша второго уровня: перед использованием кэша второго уровня необходимо настроить провайдер кэша второго уровня. JPA Hibernate поддерживает несколько провайдеров кэша второго уровня, таких как Ehcache, Infinispan, Hazelcast и др. Конфигурация провайдера может зависеть от используемого провайдера, но обычно включает указание имени провайдера, URL или других параметров соединения, настроек кэша и т.д.
2. Включение кэша второго уровня: кэш второго уровня по умолчанию отключен, поэтому необходимо явно включить его для каждой сущности или запроса. Это может быть выполнено с помощью аннотации @Cacheable для сущностей или аннотации @Cache для запросов.
3. Настройка кэша для каждой сущности: каждая сущность может иметь собственный кэш, поэтому необходимо настроить параметры кэша для каждой сущности. Это может быть выполнено с помощью аннотаций @Cache и @CacheConcurrencyStrategy для каждой сущности.
4. Использование кэша второго уровня в запросах: после включения кэша второго уровня для запросов можно использовать различные типы кэша второго уровня для хранения результатов запросов. Например, тип CacheConcurrencyStrategy.READ_ONLY позволяет сохранить результаты только для чтения запросов, тогда как тип CacheConcurrencyStrategy.TRANSACTIONAL может использоваться для сохранения результатов запросов в рамках транзакции.
5. Управление кэшем: кэш второго уровня может быть очищен или перезапущен в любое время, что может быть полезно в случае изменения данных в базе данных или при возникновении проблем с кэшем. Для управления кэшем можно использовать методы SessionFactory.getCache() и Cache.evict().

Например, чтобы получить сущность из кеша второго уровня, можно использовать метод EntityManager.find(). Если запрошенная сущность находится в кеше второго уровня, она будет возвращена из кеша, в противном случае Hibernate загрузит сущность из базы данных и поместит ее в кеш.

Также можно использовать кастомные запросы с аннотацией @NamedQuery или Criteria API, чтобы получить данные из кеша второго уровня.

Важно понимать, что кэш второго уровня может быть неактуальным, если данные в базе данных были изменены в другом месте. Поэтому необходимо тщательно настраивать кэш и обновлять его в соответствии с требованиями приложения.

74
Q

Что такое JPQL/HQL и чем он отличается от SQL?

A

Hibernate Query Language (HQL) и Java Persistence Query Language (JPQL) **являются объектно-ориентированными языками запросов, схожими по природе с SQL. **

JPQL – это адаптированный под JPA HQL .

JPQL – это язык запросов, практически такой же, как SQL, но вместо имен и колонок таблиц базы данных использует имена классов Entity и их атрибуты. В качестве параметров запросов используются типы данных атрибутов Entity, а не полей баз данных.
В отличие от SQL в JPQL есть автоматический полиморфизм, то есть каждый запрос к Entity возвращает не только объекты этого Entity, но и объекты всех его классов-потомков, независимо от стратегии наследования.
Чтобы исключить такое поведение используется функция TYPE в where условии (например
select * from Animal a where TYPE(a) IN(Animal, Cat) уже не вернет объекты класса Dog)

В JPA запрос представлен в виде javax.persistence.Query или javax.persistence.TypedQuery, полученных из EntityManager.

Основное отличие JPQL/HQL от SQL заключается в том, что JPQL/HQL оперирует не на таблицах и столбцах базы данных, а на объектах Java, представляющих сущности и связи между ними. Это позволяет разработчикам более естественным образом формулировать запросы к базе данных, используя знакомые им конструкции Java, такие как классы, поля, методы и т.д.

Другое отличие JPQL/HQL от SQL состоит в том, что JPQL/HQL предоставляет независимую от базы данных абстракцию от данных, что означает, что запросы можно писать независимо от используемой базы данных. В отличие от SQL, которая зависит от конкретной реализации СУБД и ее диалекта, JPQL/HQL обеспечивает абстракцию над данными, что делает его более гибким и переносимым.

Наконец, JPQL/HQL поддерживает концепцию объектных графов и каскадной загрузки, что позволяет загружать целые графы объектов в одном запросе, что повышает производительность и уменьшает количество запросов к базе данных. В SQL для получения таких результатов необходимо использовать множество JOIN-ов и подзапросов, что делает запросы менее читабельными и сложными для отладки.

В Hibernate HQL-запрос представлен org.hibernate.query.Query, полученный из Session. Если HQL является именованным запросом, то будет использоваться Session#getNamedQuery, в противном случае требуется Session#createQuery.

JOIN FETCH vs Join
При запросе JOIN emp.department dep возвращается только сам запрощенный объект. При запросе JOIN FETCH emp.department dep возвращается объект и связанные с ним сущности, в одном запросе.

Основные особенности HQL:

  • Оперирует на классах Java вместо таблиц и столбцов базы данных;
  • Позволяет использовать различные операции сравнения, функции, агрегатные функции, группировки и т.д.;
  • Поддерживает каскадную загрузку связанных объектов, что позволяет загружать целые графы объектов в одном запросе;
  • Поддерживает операции INSERT, UPDATE и DELETE для изменения данных в базе данных;
  • Позволяет использовать параметры запроса, что позволяет динамически формировать запросы в зависимости от внешних условий.
    Пример HQL запроса:
    from Customer c where c.city = :city order by c.lastName
75
Q

Разница между Query, NamedQuery и NativeQuery в Hibernate?

A

Разница между Query, NamedQuery и NativeQuery в Hibernate заключается в том, как они формулируют запросы к базе данных.
Объект Query позволяет создавать запросы на языке HQL (Hibernate Query Language) или JPQL (Java Persistence Query Language), которые могут быть изменены во время выполнения запроса. Query возвращает список объектов, удовлетворяющих заданным критериям.
~~~
Session session = sessionFactory.openSession();
Query query = session.createQuery(“from Customer where name like :name”);
query.setParameter(“name”, “John%”);
List<Customer> customers = query.list();
session.close();
~~~
NamedQuery - это тип запроса, который определен в метаданных приложения. Он используется для ссылки на запрос, который был определен в аннотации @NamedQuery для соответствующей сущности в JPA.
NamedQuery - это запрос, который определен в аннотации @NamedQuery в классе сущности. Он может быть вызван из любого места приложения по имени.
Названия для именованных запросов должны быть уникальными в пределах приложения. Они часто используются для запросов, которые выполняются несколько раз или для запросов с множеством параметров, таких как запрос
Пример:
~~~
@Entity
@NamedQuery(
name = "findPersonByName",
query = "from Person p where p.name = :name"
)
public class Person {
// ...
}
~~~
Использование именованного запроса:
~~~
TypedQuery<Person> query = session.createNamedQuery("findPersonByName", Person.class);
query.setParameter("name", "John");
List<Person> results = query.getResultList();
~~~
NativeQuery используется для выполнения запросов SQL, которые могут быть специфичными для базы данных.
NativeQuery позволяет написать SQL-запрос, который будет выполнен напрямую на базе данных.
Пример:
~~~
String sql = "SELECT * FROM person WHERE name = :name";
Query query = session.createNativeQuery(sql);
query.setParameter("name", "John");
List<Object[]> results = query.getResultList();
~~~</Person></Person></Customer>

76
Q

Что такое Criteria API и для чего он используется?

A

Для операций update, delete или других DDL манипуляций использовать Criteria API нельзя. Критерии используются только для выборки из базы данных в более объектно-ориентированном стиле.

Основные преимущества JPA Criteria API:
- ошибки могут быть обнаружены во время компиляции;
- позволяет динамически формировать запросы на этапе выполнения приложения.
- Более объектно-ориентированный язык выборки.
- можно применить агрегатные функции, например: criteria.setProjection(Projections.sum(“salary”));

Criteria API - это высокоуровневый интерфейс для создания динамических запросов в JPA/Hibernate. Он позволяет строить запросы на основе модели объектов и без использования языка SQL или JPQL/HQL.

Criteria API позволяет создавать запросы на основе критериев (criteria) и предикатов (predicate), которые задают условия выборки. Это удобно для создания запросов с динамически изменяемыми условиями выборки, которые зависят от пользовательского ввода или других факторов.

Кроме того, Criteria API позволяет строить запросы, которые возвращают не только сущности, но и другие типы данных, такие как агрегированные значения (например, суммы, средние значения), результаты проекций (projection) и т.д.

Ниже приведен пример использования Criteria API для создания запроса на выборку всех сотрудников с определенной зарплатой:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
Root<Employee> employee = query.from(Employee.class);
query.select(employee)
     .where(cb.equal(employee.get("salary"), 50000));
List<Employee> employees = entityManager.createQuery(query).getResultList();
77
Q

Расскажите про проблему N+1 Select и путях ее решения.

A

Проблема N+1 Select - это ситуация, когда Hibernate выполняет N+1 SQL-запросов к базе данных для загрузки связанных объектов, где N - это количество объектов, которые нужно загрузить, а +1 - это дополнительный запрос на загрузку каждого связанного объекта.

Существует несколько способов решения проблемы N+1 Select в Hibernate:

Fetch Joins - это механизм, который позволяет загрузить связанные объекты в одном запросе, используя оператор JOIN.
И при FetchType.EAGER, и при FetchType.LAZY поможет JPQL-запрос с JOIN FETCH. Опцию «FETCH» можно использовать в JOIN (INNER JOIN или LEFT JOIN) для выборки связанных объектов в одном запросе вместо дополнительных запросов для каждого доступа к ленивым полям объекта.
Это лучший вариант решения для простых запросов (1-3 уровня вложенности связанных объектов).
select pc from PostComment pc join fetch pc.post p
Например:
List<Order> orders = session.createQuery("SELECT o FROM Order o JOIN FETCH o.customer", Order.class).getResultList();
Batch Size - это параметр, который позволяет определить количество объектов, которые должны быть загружены в одном запросе.
Хотя использовать @BatchSize лучше, чем столкнуться с проблемой запроса N+1, в большинстве случаев гораздо лучшей альтернативой является использование DTO или JOIN FETCH, поскольку они позволяют получать все необходимые данные одним запросом.
Например:
~~~
@OneToMany(mappedBy = “order”)
@BatchSize(size = 10)
private List<OrderLineItem> lineItems;
~~~
**Entity Graphs** - это инструмент, который позволяет определить, какие связанные объекты должны быть загружены в одном запросе. Entity Graphs позволяют определить, какие атрибуты должны быть загружены сразу при запросе к базе данных, а какие - отложены до обращения к ним.
Если нужно получить много данных через jpql-запрос, лучше всего использовать EntityGraph.
Например:
~~~
EntityGraph<Order> graph = entityManager.createEntityGraph(Order.class);
graph.addAttributeNodes("customer");
List<Order> orders = entityManager.createQuery("SELECT o FROM Order o", Order.class)
.setHint("javax.persistence.fetchgraph", graph)
.getResultList();
~~~
**HQL Join Fetch** - это запрос на языке HQL, который использует оператор JOIN FETCH для загрузки связанных объектов в одном запросе. Например:
`List<Order> orders = session.createQuery("SELECT o FROM Order o JOIN FETCH o.customer", Order.class).getResultList();`
**Criteria API** - это API для создания запросов на языке Java. Criteria API позволяет создавать запросы, которые используют механизм Fetch Joins для загрузки связанных объектов в одном запросе. Например:
~~~
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Order> query = builder.createQuery(Order.class);
Root<Order> orderRoot = query.from(Order.class);
orderRoot.fetch("customer");
List<Order> orders = session.createQuery(query).getResultList();
~~~</Order></Order></Order></Order></Order></Order></OrderLineItem>

78
Q

Что такое EntityGraph? Как и для чего их использовать?

A

Основная цель JPA Entity Graph - улучшить производительность в рантайме при загрузке базовых полей сущности и связанных сущностей и коллекций.

Два режима работы Entity Graph
fetch - выборка только графа
load - граф + EAGER

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

Вкратце, Hibernate загружает весь граф в одном SELECT-запросе, то есть все указанные связи от нужной нам сущности:

Для использования EntityGraph необходимо создать объект EntityGraph и определить, какие атрибуты и связанные сущности должны быть загружены. Затем этот объект можно передать в метод createQuery() или createNamedQuery() для выполнения запроса с использованием заданного графа.

79
Q

EntityGraph пример реализации.

A

Для использования EntityGraph в JPA необходимо выполнить следующие шаги:

  1. Создать класс-сущность, для которой требуется оптимизировать запросы к базе данных.
  2. Определить связи с другими классами-сущностями, которые также могут быть загружены вместе с основной сущностью.
  3. Создать объект EntityGraph и добавить в него атрибуты и связи, которые должны быть загружены при выполнении запроса.
  4. Передать объект EntityGraph в метод createQuery() или createNamedQuery() при выполнении запроса к базе данных.

Например, если у нас есть класс-сущность “Заказ”, который связан с классом-сущностью “Клиент”, то мы можем создать EntityGraph для загрузки заказов вместе с информацией о клиентах. Для этого мы создадим объект EntityGraph и добавим в него атрибут “client”, который указывает на связь между заказом и клиентом. Затем мы передадим этот объект в метод createQuery() или createNamedQuery() при выполнении запроса.

Пример использования EntityGraph:

@Entity
public class Order {
    @Id
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    private Customer customer;
    // ... other attributes and methods
}

@Entity
public class Customer {
    @Id
    private Long id;
    // ... other attributes and methods
}

// Create an EntityGraph to load orders with customers
EntityGraph<Order> graph = entityManager.createEntityGraph(Order.class);
graph.addAttributeNodes("customer");

// Create a query to load orders with customers using the EntityGraph
TypedQuery<Order> query = entityManager.createQuery(
    "SELECT o FROM Order o",
    Order.class
);
query.setHint("javax.persistence.fetchgraph", graph);

// Execute the query and get the results
List<Order> orders = query.getResultList();

В этом примере мы создали EntityGraph для загрузки заказов вместе с информацией о клиентах. Затем мы создали запрос, который использует EntityGraph для загрузки данных. При выполнении запроса будут загружены только те связанные сущности, которые указаны в EntityGraph, что может улучшить производительность и уменьшить нагрузку на базу данных.