Рубрики
Без рубрики

Контекст сохранения JPA/гибернации

Узнайте о контексте сохранения с помощью Hibernate

Автор оригинала: baeldung.

1. Обзор

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

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

2. Контекст сохранения

Давайте взглянем на официальное определение Контекста сохранения :

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

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

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

EntityManager – это интерфейс, который позволяет нам взаимодействовать с контекстом сохранения. Всякий раз , когда мы используем EntityManager , мы фактически взаимодействуем с контекстом сохранения .

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

3. Тип контекста Сохранения

Контексты сохранения доступны в двух типах:

  • Контекст сохранения в области транзакций
  • Контекст сохранения с расширенной областью действия

Давайте взглянем на каждого из них.

3.1 Контекст сохранения в области транзакций

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

диаграмма контекста сохранения транзакций

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

По умолчанию типом контекста сохранения является |/PersistenceContextType.ТРАНЗАКЦИЯ . Чтобы указать EntityManager использовать контекст сохранения транзакций, мы просто аннотируем его с помощью @PersistenceContext :

@PersistenceContext
private EntityManager entityManager;

3.2 Контекст сохранения с расширенной областью действия

Расширенный контекст сохранения может охватывать несколько транзакций. Мы можем сохранить сущность без транзакции, но не можем удалить ее без транзакции.

диаграмма контекста расширенной сохраняемости

Чтобы указать EntityManager использовать контекст сохранения с расширенной областью действия, нам нужно применить тип атрибут @PersistenceContext :

@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;

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

Допустим , мы сохраняем некоторую сущность в методе компонента A , который выполняется в транзакции. Затем мы вызываем некоторый метод компонента B . В контексте сохранения метода компонента B мы не найдем сущность, которую мы сохраняли ранее в методе компонента A .

4. Пример Контекста Сохранения

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

Во-первых, давайте создадим наш класс обслуживания, Контекстный пользовательский сервис сохранения транзакций :

@Component
public class TransctionPersistenceContextUserService {

    @PersistenceContext
    private EntityManager entityManager;
    
    @Transactional
    public User insertWithTransaction(User user) {
        entityManager.persist(user);
        return user;
    }
    
    public User insertWithoutTransaction(User user) {
        entityManager.persist(user);
        return user;
    }
    
    public User find(long id) {
        return entityManager.find(User.class, id);
    }
}

Следующий класс, Расширенная служба пользователя PersistenceContext , очень похож на описанный выше, за исключением аннотации @PersistenceContext . На этот раз мы передаем PersistenceContextType.РАСШИРЕНО в тип параметр его @PersistenceContext аннотации:

@Component
public class ExtendedPersistenceContextUserService {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager entityManager;

    // Remaining code same as above
}

5. Тестовые примеры

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

5.1 Тестирование Контекста Сохранения транзакций

Давайте сохраним сущность User , используя контекст сохранения в области транзакций. Объект будет сохранен в постоянном хранилище. Затем мы проверяем, выполнив вызов find, используя наш расширенный контекст сохранения EntityManager :

User user = new User(121L, "Devender", "admin");
transctionPersistenceContext.insertWithTransaction(user);

User userFromTransctionPersistenceContext = transctionPersistenceContext
  .find(user.getId());
assertNotNull(userFromTransctionPersistenceContext);

User userFromExtendedPersistenceContext = extendedPersistenceContext
  .find(user.getId());
assertNotNull(userFromExtendedPersistenceContext);

Когда мы попытаемся вставить Пользователя сущность без транзакции, будет выдано исключение TransactionRequiredException :

@Test(expected = TransactionRequiredException.class)
public void testThatUserSaveWithoutTransactionThrowException() {
    User user = new User(122L, "Devender", "admin");
    transctionPersistenceContext.insertWithoutTransaction(user);
}

5.2 Тестирование Расширенного Контекста Сохранения

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

User user = new User(123L, "Devender", "admin");
extendedPersistenceContext.insertWithoutTransaction(user);

User userFromExtendedPersistenceContext = extendedPersistenceContext
  .find(user.getId());
assertNotNull(userFromExtendedPersistenceContext);

User userFromTransctionPersistenceContext = transctionPersistenceContext
  .find(user.getId());
assertNull(userFromTransctionPersistenceContext);

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

@Test(expected = EntityExistsException.class)
public void testThatPersistUserWithSameIdentifierThrowException() {
    User user1 = new User(126L, "Devender", "admin");
    User user2 = new User(126L, "Devender", "admin");
    extendedPersistenceContext.insertWithoutTransaction(user1);
    extendedPersistenceContext.insertWithoutTransaction(user2);
}

Мы увидим Исключение EntityExistsException :

javax.persistence.EntityExistsException: 
A different object with the same identifier value
was already associated with the session

Расширенный контекст сохранения в транзакции сохраняет сущность в постоянном хранилище в конце транзакции:

User user = new User(127L, "Devender", "admin");
extendedPersistenceContext.insertWithTransaction(user);

User userFromDB = transctionPersistenceContext.find(user.getId());
assertNotNull(userFromDB);

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

User user1 = new User(124L, "Devender", "admin");
extendedPersistenceContext.insertWithoutTransaction(user1);

User user2 = new User(125L, "Devender", "admin");
extendedPersistenceContext.insertWithTransaction(user2);

User user1FromTransctionPersistenceContext = transctionPersistenceContext
  .find(user1.getId());
assertNotNull(user1FromTransctionPersistenceContext);

User user2FromTransctionPersistenceContext = transctionPersistenceContext
  .find(user2.getId());
assertNotNull(user2FromTransctionPersistenceContext);

6. Заключение

В этом уроке мы получили хорошее представление о контексте сохранения.

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

Как всегда, пример кода доступен на GitHub .