Объединить с несуществующим идентификатором

Как вы все знаете, EntityManager.merge () был введен для объединения изменений, сделанных для отдельного объекта, в присоединенный. Это предполагает, что шаблон «извлекает сущность, возвращает свою мелкую копию некоторому клиенту, позволяет клиенту внести некоторые изменения и отправить нам обновленный экземпляр, а затем объединить обновленный экземпляр обратно в контекст сохранения», правильно?

Но что, если экземпляр был удален третьей стороной, пока этот клиент играл со своей отдельной копией? Когда мы попытаемся объединить его обратно, можно ожидать, что он будет вести себя аналогично обновлению SQL: выведите исключение из вида «entity не существует». Один из них был бы разочарован: JPA (по крайней мере, в реализации Hibernate) просто проигнорирует предоставленный идентификатор в этом случае и создаст новый объект вместо обновления существующего.

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

Я предполагаю, что я могу найти объект по id перед выполнением слияния (throw if not found) и полагаться на «повторяемое чтение», чтобы предотвратить удаление объекта (если оно есть), чтобы стать видимым для текущей транзакции, гарантируя, что merge () обновит существующий объект. Это немного уродливо, особенно учитывая, как merge () может каскадироваться для вложенных объектов, и мне также придется найти () их. Я также попытался представить @version, надеясь, что это поможет, и JPA обнаружит, что неправильно создавать новый объект, если я поставляю ненулевую базовую версию, но это тоже игнорировалось.

PS Мне просто показалось, что я могу позволить JPA создать новый объект, а затем проверить его идентификатор на тот, который предоставляется клиентом. Если, скажем, для создания идентификаторов используется последовательность, новый идентификатор будет отличаться от старого, и в этом случае я могу отбросить исключение и отменить транзакцию. Но это еще дополнительная проверка, поэтому это решение не лучше, чем при использовании find ().

hibernate,jpa,

1

Ответов: 1


1

Вы можете проверить после выполнения слияния, если ключ тот же.
Если это то же самое, это означает, что объединенный объект тот же, если нет, это означает, что удаленный объект был удален.

    EntityManager em = entityManagerFactory.createEntityManager();
    em.getTransaction().begin();
    EntityClass mergedEntity = em.merge( detachedEntity );
    if( mergedEntity.getId() != detachedEntity.getId() ){
        System.out.println("Entity has been deleted !!!  Do rollback.");
        em.getTransaction().rollback();
    }else{
        System.out.println("Entity has been merged !!! Do commit ");
        em.getTransaction().commit();
    }

Если вы включите трассировку SQL, вы увидите, что во время выполнения операции слияния:
mergedEntity = em.merge( detachedEntity );Hibernate под капотом делает:

  • SELECT ... FROM Entity WHERE id = id_of_detached_entity
  • если приведенный выше запрос находит строку, то Hibernate анализирует один и тот же идентификатор для объекта объединенной сущности
  • если вышеприведенный запрос ничего не возвращает, Hibernate выбирает следующее значение Id из последовательности и присваивает это значение объединенному объекту сущности, поэтому объединенный объект имеет другой Id, старый, отсоединенный объект.
спящий режим, JPA,
Похожие вопросы