TL; DR
T findOne(ID id)
(имя в старом API) / Optional<T> findById(ID id)
(имя в новом API) зависит от того, EntityManager.find()
что выполняет загрузку объекта .
T getOne(ID id)
полагается на EntityManager.getReference()
то, что объект выполняет ленивую загрузку . Таким образом, чтобы обеспечить эффективную загрузку объекта, необходимо вызвать метод для него.
findOne()/findById()
действительно более понятный и простой в использовании, чем getOne()
.
Так что в большинстве случаев благосклонность findOne()/findById()
закончена getOne()
.
Изменение API
По крайней мере, 2.0
версия, Spring-Data-Jpa
модифицированная findOne()
.
Ранее это было определено в CrudRepository
интерфейсе как:
T findOne(ID primaryKey);
Теперь единственный findOne()
метод, который вы найдете в CrudRepository
том, который определен в QueryByExampleExecutor
интерфейсе как:
<S extends T> Optional<S> findOne(Example<S> example);
Наконец SimpleJpaRepository
, это реализовано реализацией CrudRepository
интерфейса по умолчанию .
Этот метод является запросом по примеру поиска, и вы не хотите использовать его в качестве замены.
Фактически, метод с таким же поведением все еще присутствует в новом API, но имя метода изменилось.
Он был переименован из findOne()
к findById()
в CrudRepository
интерфейсе:
Optional<T> findById(ID id);
Теперь он возвращает Optional
. Что не так плохо, чтобы предотвратить NullPointerException
.
Итак, фактический выбор теперь между Optional<T> findById(ID id)
и T getOne(ID id)
.
Два разных метода, основанные на двух разных методах поиска JPA EntityManager
1) Optional<T> findById(ID id)
Javadoc утверждает, что это:
Извлекает объект по его идентификатору.
Когда мы посмотрим на реализацию, мы увидим, что она зависит от EntityManager.find()
выполнения поиска:
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}
И здесь em.find()
приведен EntityManager
метод , объявленный как:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Его Javadoc утверждает:
Найти по первичному ключу, используя указанные свойства
Таким образом, получение загруженного объекта кажется ожидаемым.
2) В то время как T getOne(ID id)
Javadoc утверждает (акцент мой):
Возвращает ссылку на сущность с заданным идентификатором.
Фактически, эталонная терминология действительно является платой, и JPA API не определяет какой-либо getOne()
метод.
Поэтому лучше всего понять, что делает оболочка Spring, - посмотреть на реализацию:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Здесь em.getReference()
приведен EntityManager
метод , объявленный как:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
И, к счастью, EntityManager
Javadoc лучше определил свое намерение (акцент мой):
Получить экземпляр, состояние которого можно лениво извлекать . Если запрошенный экземпляр не существует в базе данных, EntityNotFoundException генерируется при первом обращении к состоянию экземпляра . (Среде выполнения поставщика сохраняемости разрешается выбрасывать исключение EntityNotFoundException при вызове getReference.) Приложение не должно ожидать, что состояние экземпляра будет доступно после отсоединения , если только оно не было доступно приложению, пока был открыт менеджер сущностей.
Таким образом, вызов getOne()
может вернуть лениво извлеченную сущность.
Здесь ленивое извлечение относится не к отношениям сущности, а к самой сущности.
Это означает, что если мы вызываем, getOne()
а затем контекст постоянства закрывается, сущность может никогда не загрузиться, и поэтому результат действительно непредсказуем.
Например, если прокси-объект сериализуется, вы можете получить null
ссылку как сериализованный результат или, если метод прокси-объекта вызывается, исключение, такое какLazyInitializationException
выдается.
Таким образом, в такой ситуации выбрасывание EntityNotFoundException
этого является основной причиной использования getOne()
для обработки экземпляра, который не существует в базе данных, поскольку ситуация ошибки может никогда не выполняться, пока объект не существует.
В любом случае, чтобы гарантировать его загрузку, вы должны манипулировать сущностью во время открытия сеанса. Вы можете сделать это, вызвав любой метод на объекте.
Или лучшее альтернативное использование findById(ID id)
вместо.
Почему такой неясный API?
В заключение два вопроса для разработчиков Spring-Data-JPA:
почему нет более четкой документации для getOne()
? Сущность ленивой загрузки действительно не является деталью.
зачем вводить, getOne()
чтобы обернуть EM.getReference()
?
Почему бы не просто придерживаться обернутой методы: getReference()
? Этот EM-метод действительно очень специфичен, хотя и getOne()
обеспечивает очень простую обработку.