1. Введение
EntityManager
является частью Java Persistence API. Главным образом, он реализует программные интерфейсы и правила жизненного цикла, определенные спецификацией JPA 2.0.
Более того, мы можем получить доступ к Persistence Context с помощью API в EntityManager
.
В этом руководстве мы рассмотрим конфигурацию, типы и различные API- интерфейсы EntityManager
.
2. Зависимости Maven
Во-первых, нам нужно включить зависимости Hibernate:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.0.Final</version>
</dependency>
В зависимости от используемой базы данных нам также потребуется включить зависимости драйвера:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
Зависимости hibernate-core и mysql-connector-java доступны на Maven Central.
3. Конфигурация
Теперь давайте продемонстрируем EntityManager
, используя сущность Movie
, которая соответствует таблице MOVIE в базе данных.
В этой статье мы будем использовать EntityManager
API для работы с объектами Movie в базе данных.
3.1. Определение объекта
Начнем с создания объекта, соответствующего таблице MOVIE, с помощью аннотации @Entity
:
@Entity
@Table(name = "MOVIE")
public class Movie {
@Id
private Long id;
private String movieName;
private Integer releaseYear;
private String language;
// standard constructor, getters, setters
}
3.2. Файл persistence.xml
_
При создании EntityManagerFactory реализация
постоянства ищет файл META-INF/persistence.xml
в пути к классам .
Этот файл содержит конфигурацию для EntityManager
:
<persistence-unit name="com.foreach.movie_catalog">
<description>Hibernate EntityManager Demo</description>
<class>com.foreach.hibernate.pojo.Movie</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/moviecatalog"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
</properties>
</persistence-unit>
Как мы видим, мы определяем единицу персистентности, которая определяет базовое хранилище данных, управляемое EntityManager
.
Кроме того, мы определяем диалект и другие свойства JDBC базового хранилища данных. Hibernate не зависит от базы данных. На основе этих свойств Hibernate подключается к базовой базе данных.
4. EntityManager, управляемый контейнером и приложением
По сути, существует два типа EntityManager
: управляемый контейнером и управляемый приложением.
Рассмотрим подробнее каждый тип.
4.1. EntityManager
, управляемый контейнером ``
Здесь контейнер внедряет EntityManager
в наши корпоративные компоненты.
Другими словами, контейнер создает для нас EntityManager
из EntityManagerFactory
:
@PersistenceContext
EntityManager entityManager;
Это также означает , что контейнер отвечает за начало транзакции, а также за ее фиксацию или откат.
Точно так же контейнер отвечает за закрытие EntityManager,
поэтому его безопасно использовать `без ручной очистки. Даже если мы попытаемся
закрыть
EntityManager , управляемый контейнером , он должен
вызвать исключение IllegalStateException.`
4.2. EntityManager
, управляемый приложением ``
И наоборот, жизненный цикл EntityManager
управляется приложением.
На самом деле мы вручную создадим EntityManager
, а также будем управлять его жизненным циклом.
Во-первых, давайте создадим EntityManagerFactory:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.foreach.movie_catalog");
Чтобы создать EntityManager
, мы должны явно вызвать createEntityManager()
в EntityManagerFactory
:
public static EntityManager getEntityManager() {
return emf.createEntityManager();
}
Поскольку мы несем ответственность за создание экземпляров EntityManager
, мы также несем ответственность за их закрытие .
Поэтому мы должны закрыть
каждый EntityManager
, когда закончим их использовать.
4.3. Потокобезопасность
Экземпляры EntityManagerFactory
и, следовательно, экземпляры SessionFactory
Hibernate являются потокобезопасными . Так что в параллельных контекстах совершенно безопасно писать:
EntityManagerFactory emf = // fetched from somewhere
EntityManager em = emf.createEntityManager();
С другой стороны, экземпляры EntityManager
не являются потокобезопасными и предназначены для использования в средах, ограниченных потоками . Это означает, что каждый поток должен получить свой экземпляр, работать с ним и закрыть его в конце.
При использовании EntityManager
, управляемого приложением , легко создавать экземпляры, ограниченные потоками:
EntityManagerFactory emf = // fetched from somewhere
EntityManager em = emf.createEntityManager();
// use it in the current thread
Однако при использовании EntityManager
, управляемого контейнером, все становится нелогичным :
@Service
public class MovieService {
@PersistenceContext // or even @Autowired
private EntityManager entityManager;
// omitted
}
Кажется, что один экземпляр EntityManager
должен быть общим для всех операций. Однако контейнер (JakartaEE или Spring) вместо простого EntityManager
здесь внедряет специальный прокси . Spring, например, внедряет прокси типа SharedEntityManagerCreator .
Каждый раз, когда мы используем внедренный EntityManager,
этот прокси будет либо повторно использовать существующий EntityManager
, либо создавать новый. Повторное использование обычно происходит, когда мы включаем что-то вроде Open Session/EntityManager в View .
В любом случае контейнер гарантирует, что каждый EntityManager
ограничен одним потоком .
5. Спящие операции сущностей
EntityManager
API предоставляет набор методов. Мы можем взаимодействовать с базой данных, используя эти методы.
5.1. Сохраняющиеся сущности
Чтобы иметь объект, связанный с EntityManager, мы можем использовать метод persist()
:
public void saveMovie() {
EntityManager em = getEntityManager();
em.getTransaction().begin();
Movie movie = new Movie();
movie.setId(1L);
movie.setMovieName("The Godfather");
movie.setReleaseYear(1972);
movie.setLanguage("English");
em.persist(movie);
em.getTransaction().commit();
}
Как только объект сохранен в базе данных, он находится в постоянном
состоянии.
5.2. Загрузка объектов
Для извлечения объекта из базы данных мы можем использовать метод find()
.
Здесь метод ищет по первичному ключу. На самом деле метод ожидает тип класса сущности и первичный ключ:
public Movie getMovie(Long movieId) {
EntityManager em = getEntityManager();
Movie movie = em.find(Movie.class, new Long(movieId));
em.detach(movie);
return movie;
}
Однако, если нам просто нужна ссылка на объект, мы можем вместо этого использовать метод getReference()
. По сути, он возвращает объекту прокси:
Movie movieRef = em.getReference(Movie.class, new Long(movieId));
5.3. Отсоединение объектов
В случае, если нам нужно отсоединить сущность от контекста персистентности, мы можем использовать метод detach ()
. Мы передаем отсоединяемый объект в качестве параметра методу:
em.detach(movie);
Как только объект будет отсоединен от контекста постоянства, он будет в отсоединенном состоянии.
5.4. Объединение объектов
На практике во многих приложениях требуется изменение объекта для нескольких транзакций. Например, мы можем захотеть получить объект в одной транзакции для отображения в пользовательском интерфейсе. Затем другая транзакция внесет изменения, внесенные в пользовательский интерфейс.
Для таких ситуаций мы можем использовать метод merge()
. Метод слияния помогает перенести любые изменения, внесенные в отсоединенный объект, в управляемый объект:
public void mergeMovie() {
EntityManager em = getEntityManager();
Movie movie = getMovie(1L);
em.detach(movie);
movie.setLanguage("Italian");
em.getTransaction().begin();
em.merge(movie);
em.getTransaction().commit();
}
5.5. Запрос сущностей
Кроме того, мы можем использовать JPQL для запроса сущностей. Мы будем вызывать getResultList()
для их выполнения.
Конечно, мы можем использовать getSingleResult()
, если запрос возвращает только один объект:
public List<?> queryForMovies() {
EntityManager em = getEntityManager();
List<?> movies = em.createQuery("SELECT movie from Movie movie where movie.language = ?1")
.setParameter(1, "English")
.getResultList();
return movies;
}
5.6. Удаление объектов
Кроме того, мы можем удалить объект из базы данных с помощью метода remove()
. Важно отметить, что объект не отсоединяется, а удаляется.
Здесь состояние сущности меняется с постоянного на новое:
public void removeMovie() {
EntityManager em = HibernateOperations.getEntityManager();
em.getTransaction().begin();
Movie movie = em.find(Movie.class, new Long(1L));
em.remove(movie);
em.getTransaction().commit();
}
6. Заключение
В этой статье мы рассмотрели EntityManager
в Hibernate. Мы рассмотрели типы и конфигурацию и узнали о различных методах, доступных в API для работы с Persistence Context.
Как всегда, код, использованный в этой статье, доступен на Github .