Подтвердить что ты не робот

Зачем использовать возвращенный экземпляр после save() в репозитории Spring Data JPA?

Вот код:

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}

JpaRepository из проекта данных JPA.

Вот код тестирования:

public class JpaAccountRepositoryTest extends JpaRepositoryTest {
    @Inject
    private AccountRepository accountRepository;

    @Inject
    private Account account;

    @Test
    @Transactional
    public void createAccount() {
        Account returnedAccount = accountRepository.save(account);

        System.out.printf("account ID is %d and for returned account ID is %d\n", account.getId(), returnedAccount.getId());
    }
}

Вот результат:

account ID is 0 and for returned account ID is 1

Вот от CrudReporsitory.save() javadoc:

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

Вот фактический код для SimpleJpaRepository из Spring Данные JPA:

 @Transactional
    public T save(T entity) { 
            if (entityInformation.isNew(entity)) {
                    em.persist(entity);
                    return entity;
            } else {
                    return em.merge(entity);
            }
    }

Итак, возникает вопрос, почему нам нужно использовать возвращаемый экземпляр вместо исходного? (да, мы должны это сделать, иначе мы продолжим работать с отдельным экземпляром, но почему)

Исходный метод EntityManager.persist() возвращает void, поэтому наш экземпляр привязан к контексту персистентности. Происходит ли какая-либо прокси-магия при передаче учетной записи для сохранения в репозиторий? Является ли это ограничение архитектуры проекта Spring Data JPA?

4b9b3361

Ответ 1

Метод save(…) интерфейса CrudRepository должен абстрагироваться от простого хранения сущности независимо от того, в каком состоянии он находится. Таким образом, он не должен предоставлять фактическую реализацию конкретного хранилища, даже если (как в JPA) хранилище различает новые сущности для хранения и существующие для обновления. Вот почему метод фактически называется save(…) не create(…) или update(…). Мы возвращаем результат из этого метода, чтобы фактически позволить реализации магазина возвращать совершенно другой экземпляр, как это делает JPA, когда вызывается merge(…).

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

Ответ 2

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