Использование EntityManager поверх репозиториев данных Spring

Недавно я наткнулся на следующий фрагмент кода:

@Transactional
public MyEntity insert(MyEntity entity) {
    MyEntity merged = entityManager.merge(entity);
    return myEntityRepository.save(merged);
}

где менеджер сущности определяется следующим образом:

@PersistenceContext private EntityManager entityManager;

а репозиторий - репозиторий Spring QueryDSL:

@Repository
public interface MyEntityRepository extends QueryDslRepository<MyEntity>{
}

Мой вопрос: действительно ли необходимо вызывать entityManager.merge(entity), когда мы сохраняем сущность, используя myEntityRepository сразу после? Есть ли что-то, что делает entityManager, чего не может репозиторий? Разве вызова репозитория не должно быть достаточно?


person Smajl    schedule 30.07.2018    source источник
comment
Его не следует использовать. Репозиторий делает это при необходимости: stackoverflow.com/a/38894500/1199132   -  person Xtreme Biker    schedule 30.07.2018


Ответы (2)


Мне это кажется карго-культовым программированием. реализация save() уже выполняет слияние при необходимости (а иногда и когда в этом нет необходимости):

/*
 * (non-Javadoc)
 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
 */
@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
person Jens Schauder    schedule 30.07.2018
comment
Работает ли это, когда вставленный объект содержит несколько вложенных объектов, которые также необходимо обновить? - person Smajl; 30.07.2018
comment
Не уверен, что вы подразумеваете под этим или под работой. Предполагаемое поведение merge описано в спецификации JPA. - person Jens Schauder; 30.07.2018

Как по мне, это выглядит довольно опасным кодом с немного размытым намерением и некоторым перепроектированием:

  1. Есть ли у вас 100% гарантия того, что ваш уровень и репозиторий будут использовать один и тот же менеджер транзакций? Если нет, то вы в беде.
  2. Вы делаете просто двойную работу (ответ @Jens показывает это).
  3. @Transactional здесь может только усугубить ситуацию (если у вас какая-то нестандартная политика сброса). Особенно обратите внимание, что это не сработает, если вы вызываете метод из того же класса, так как он работает через прокси.
  4. Если вы действительно собираетесь insert() (новая запись), зачем вам вообще merge()?

Мой голос - просто используйте save(), как указал @Jens. Если вам действительно нужна функциональность insert(), вам может понадобиться реальная транзакция с защитой от обновления, и в этом случае я бы сделал некоторый собственный код на уровне репозитория. Надеюсь, вам это не нужно.

person Roman Nikitchenko    schedule 30.07.2018