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);
และโชคดีที่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()
ก็ทำการประมวลผลอย่างง่าย