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)
Явадок заявляє, що це:
Отримує об'єкт за його ідентифікатором.
Переглядаючи реалізацію, ми бачимо, що вона покладається на 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);
Її джавадок говорить:
Знайдіть за первинним ключем, використовуючи вказані властивості
Отже, завантаження завантаженої сутності здається очікуваним.
2) Поки штат T getOne(ID id)
Явадок (акцент мій):
Повертає посилання на сутність із заданим ідентифікатором.
Насправді, довідкова термінологія насправді є бордовою, і 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()
:? Цей метод ЕМ насправді дуже особливий, хоча getOne()
передає настільки просту обробку.