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ระบุว่า:
ดึงข้อมูลเอนทิตี้ของ 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);
สถานะ javadoc:
ค้นหาตามคีย์หลักโดยใช้คุณสมบัติที่ระบุ
ดังนั้นการดึงเอนทิตีที่โหลดจึงดูเหมือนว่าจะเกิดขึ้น
2) ในขณะที่รัฐT getOne(ID id)javadoc (เน้นเป็นของฉัน):
ส่งคืนการอ้างอิงไปยังเอนทิตีที่มีตัวระบุที่กำหนด
อันที่จริงคำศัพท์อ้างอิงเป็นบอร์ดจริงๆและ JPA API ไม่ได้ระบุgetOne()วิธีการใด ๆ
ดังนั้นสิ่งที่ดีที่สุดที่จะทำเพื่อทำความเข้าใจในสิ่งที่เสื้อคลุมฤดูใบไม้ผลิจะมองหาการดำเนินการ:
@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()? วิธีการ EM นี้มีความเฉพาะเจาะจงมากในขณะเดียวกันgetOne() ก็ทำการประมวลผลอย่างง่าย