TL; DR
T findOne(ID id)
(tên trong API cũ) / Optional<T> findById(ID id)
(tên trong API mới) phụ thuộc vào EntityManager.find()
việc thực hiện tải thực thể háo hức .
T getOne(ID id)
dựa vào EntityManager.getReference()
đó thực hiện một tải thực thể lười biếng . Vì vậy, để đảm bảo tải thực thể hiệu quả, yêu cầu một phương thức trên nó là bắt buộc.
findOne()/findById()
thực sự rõ ràng và đơn giản hơn để sử dụng hơn getOne()
.
Vì vậy, trong hầu hết các trường hợp, ủng hộ findOne()/findById()
hơn getOne()
.
Thay đổi API
Từ ít nhất, 2.0
phiên bản, Spring-Data-Jpa
sửa đổi findOne()
.
Trước đây, nó được định nghĩa trong CrudRepository
giao diện là:
T findOne(ID primaryKey);
Bây giờ, findOne()
phương thức duy nhất mà bạn sẽ tìm thấy CrudRepository
là phương thức được xác định trong QueryByExampleExecutor
giao diện là:
<S extends T> Optional<S> findOne(Example<S> example);
Điều đó được thực hiện cuối cùng bởi SimpleJpaRepository
, việc thực hiện mặc định của CrudRepository
giao diện.
Phương pháp này là một truy vấn bằng cách tìm kiếm ví dụ và bạn không muốn thay thế nó.
Trên thực tế, phương thức có hành vi tương tự vẫn có trong API mới nhưng tên phương thức đã thay đổi.
Nó đã được đổi tên từ findOne()
thành findById()
trong CrudRepository
giao diện:
Optional<T> findById(ID id);
Bây giờ nó trả về một Optional
. Mà không phải là quá xấu để ngăn chặn NullPointerException
.
Vì vậy, sự lựa chọn thực tế là giữa Optional<T> findById(ID id)
và T getOne(ID id)
.
Hai phương thức riêng biệt dựa trên hai phương thức truy xuất JPA EntityManager riêng biệt
1) Optional<T> findById(ID id)
javadoc nói rằng:
Lấy một thực thể bằng id của nó.
Khi chúng ta xem xét việc thực hiện, chúng ta có thể thấy rằng nó dựa vào EntityManager.find()
để thực hiện truy xuất:
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));
}
Và đây em.find()
là một EntityManager
phương thức được khai báo là:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Javadoc của nó tuyên bố:
Tìm theo khóa chính, sử dụng các thuộc tính được chỉ định
Vì vậy, việc truy xuất một thực thể được tải dường như được mong đợi.
2) Trong khi các trạng thái T getOne(ID id)
javadoc (nhấn mạnh là của tôi):
Trả về một tham chiếu đến thực thể với mã định danh đã cho.
Trên thực tế, thuật ngữ tham chiếu thực sự là hội đồng quản trị và API JPA không chỉ định bất kỳ getOne()
phương pháp nào .
Vì vậy, điều tốt nhất để làm để hiểu những gì trình bao bọc Spring làm là xem xét việc thực hiện:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Đây em.getReference()
là một EntityManager
phương thức được khai báo là:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
Và may mắn thay, EntityManager
javadoc xác định rõ hơn ý định của nó (nhấn mạnh là của tôi):
Lấy một ví dụ, trạng thái có thể được lấy một cách lười biếng . Nếu phiên bản được yêu cầu không tồn tại trong cơ sở dữ liệu, EntityNotFoundException sẽ bị ném khi trạng thái thể hiện được truy cập lần đầu tiên . (Thời gian chạy của nhà cung cấp kiên trì được phép ném EntityNotFoundException khi getReference được gọi.) Ứng dụng không nên hy vọng rằng trạng thái cá thể sẽ có sẵn khi tách ra , trừ khi ứng dụng được truy cập trong khi trình quản lý thực thể được mở.
Vì vậy, gọi getOne()
có thể trả lại một thực thể tìm nạp lười biếng.
Ở đây, việc tìm nạp lười biếng không đề cập đến các mối quan hệ của thực thể mà là chính thực thể đó.
Điều đó có nghĩa là nếu chúng ta gọi getOne()
và sau đó bối cảnh Kiên trì bị đóng, thực thể có thể không bao giờ được tải và do đó kết quả thực sự không thể đoán trước.
Ví dụ: nếu đối tượng proxy được tuần tự hóa, bạn có thể nhận được một null
tham chiếu là kết quả được tuần tự hóa hoặc nếu một phương thức được gọi trên đối tượng proxy, một ngoại lệ như LazyInitializationException
được ném ra.
Vì vậy, trong tình huống này, việc ném EntityNotFoundException
nó là lý do chính để sử dụnggetOne()
để xử lý một trường hợp không tồn tại trong cơ sở dữ liệu vì tình huống lỗi có thể không bao giờ được thực hiện trong khi thực thể không tồn tại.
Trong mọi trường hợp, để đảm bảo tải của nó, bạn phải thao tác thực thể trong khi phiên được mở. Bạn có thể làm điều đó bằng cách gọi bất kỳ phương thức nào trên thực thể.
Hoặc sử dụng thay thế tốt hơn thay findById(ID id)
vì.
Tại sao một API không rõ ràng như vậy?
Để kết thúc, hai câu hỏi dành cho nhà phát triển Spring-Data-JPA:
Tại sao không có một tài liệu rõ ràng hơn cho getOne()
? Thực thể lười tải thực sự không phải là một chi tiết.
Tại sao bạn cần phải giới thiệu getOne()
để bọc EM.getReference()
?
Tại sao không chỉ đơn giản là dính vào phương pháp bọc : getReference()
? Phương pháp EM này thực sự rất đặc biệt trong khi getOne()
truyền tải một quá trình xử lý đơn giản như vậy.