Зачем использовать возвращенный экземпляр после 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?
Ответы
Ответ 1
Метод save(…)
интерфейса CrudRepository
должен абстрагироваться от простого хранения сущности независимо от того, в каком состоянии он находится. Таким образом, он не должен предоставлять фактическую реализацию конкретного хранилища, даже если (как в JPA) хранилище различает новые сущности для хранения и существующие для обновления. Вот почему метод фактически называется save(…)
не create(…)
или update(…)
. Мы возвращаем результат из этого метода, чтобы фактически позволить реализации магазина возвращать совершенно другой экземпляр, как это делает JPA, когда вызывается merge(…)
.
Поэтому, как правило, решение API должно быть снисходительным (допустимым, терпимым) в отношении фактической реализации и, следовательно, реализации метода для JPA, как мы. Никаких дополнительных прокси-сообщений не было сделано переданным объектам.
Ответ 2
Вы пропустили вторую часть: если объект не является новым, вызывается merge
. merge
копирует состояние своего аргумента в прикрепленный объект с тем же идентификатором и возвращает прикрепленный объект. Если объект не является новым и вы не используете возвращаемый объект, вы внесете изменения в отдельный объект.