Зачем использовать возвращенный экземпляр после save () в репозитории Spring Data JPA? - PullRequest
55 голосов
/ 24 декабря 2011

Вот код:

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

JpaRepository из проекта JPA Spring Data.

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

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

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

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

Вот фактический код для SimpleJpaRepository из Spring Data 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?

Ответы [ 2 ]

60 голосов
/ 26 декабря 2011

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

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

13 голосов
/ 24 декабря 2011

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

...