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);
І на щастя, EntityManagerjavadoc краще визначив свій намір (акцент мій):
Отримайте екземпляр, стан якого може бути ліниво підібраний . Якщо запитуваний екземпляр не існує в базі даних, EntityNotFoundException передається під час першого доступу до стану екземпляра . (Час виконання постачальника наполегливості дозволено викидати EntityNotFoundException, коли викликається getReference.) Додаток не повинен очікувати, що стан екземпляра буде доступним після від'єднання , якщо тільки до нього не звертався додаток, поки менеджер суб'єкта був відкритий.
Отже, виклик getOne()може повернути ліниво зібрану сутність.
Тут лінивий збір стосується не відносин суб'єкта, а самої сутності.
Це означає, що якщо ми посилаємося getOne()і тоді контекст Постійності закритий, сутність може ніколи не завантажуватися, і тому результат справді непередбачуваний.
Наприклад, якщо проксі-об'єкт серіалізований, ви можете отримати nullпосилання як серіалізований результат або якщо метод викликується на проксі-об'єкт, викид, наприклад, LazyInitializationExceptionвикидається.
Отже, у подібній ситуації викид EntityNotFoundExceptionцього є основною причиною використання getOne()для обробки екземпляра, який не існує в базі даних, оскільки ситуація з помилками ніколи не може бути виконана, поки сутність не існує.
У будь-якому випадку, щоб забезпечити його завантаження, ви повинні маніпулювати сутністю під час відкриття сеансу. Це можна зробити, скориставшись будь-яким методом сутності.
Або краще альтернативне використання findById(ID id)замість.
Чому настільки незрозумілий API?
На закінчення два питання для розробників Spring-Data-JPA:
чому б не мати більш чітку документацію getOne()? Ледачі завантаження суб'єктів справді не є деталлю.
навіщо вам вводити getOne()обгортання EM.getReference()?
Чому б просто не дотримуватися обгорнутого методу getReference():? Цей метод ЕМ насправді дуже особливий, хоча getOne() передає настільки просту обробку.