เร่งสปีด Database 10x ด้วย JPA Projections
ปัญหาคลาสสิกของคนทำ Spring Boot คือ "Over-fetching" ครับ
สมมติคุณมี Entity ชื่อ Product ซึ่งภายในมีข้อมูลเยอะมาก ทั้ง description ยาวเหยียด, blob รูปภาพ, หรือ List<Review> ที่ join กันยุ่งเหยิง
แต่โจทย์ของคุณคือ: "ทำ Dropdown ให้เลือกสินค้า (ใช้แค่ ID กับ Name)"
Code ที่ Dev 90% ชอบเขียน:
// 1. ดึงมาทั้งตาราง (SELECT * FROM product)
List<Product> products = productRepository.findAll();
// 2. วนลูป Map เป็น DTO ใน Java
List<ProductDropdownDto> dtos = products.stream()
.map(p -> new ProductDropdownDto(p.getId(), p.getName()))
.collect(Collectors.toList());
ปัญหาคืออะไร? (The Impact)
นี่คือหายนะทาง Performance ครับ
- Database: ต้องอ่านและส่งข้อมูล 100% (รวม field ที่ไม่ใช้) ผ่าน Network
- Memory: Java ต้องจอง Ram เพื่อสร้าง Object
Productตัวอ้วนๆ ขึ้นมาเป็นพันตัว - Hibernate: ต้องเสียเวลาทำ Proxy และ tracking state (Dirty Checking) ทั้งที่เราแค่จะอ่านเฉยๆ
ทางออก: ใช้ Interface-based Projections
Spring Data JPA ฉลาดกว่าที่คุณคิด มันอนุญาตให้คุณกำหนด Interface ที่มีแค่ Getter ของ field ที่คุณต้องการ แล้วมันจะเขียน SQL ให้เอง!
เปลี่ยน Code เป็นแบบนี้:
1. สร้าง Interface เปล่าๆ (Projection):
public interface ProductSummary {
Long getId();
String getName();
// ดึงแค่ 2 ค่านี้พอ จบ!
}
2. แก้ Repository ให้ return Interface นี้แทน Entity:
public interface ProductRepository extends JpaRepository<Product, Long> {
// Spring จะรู้เองว่าต้อง SELECT id, name FROM product...
List<ProductSummary> findByStatus(String status);
}
3. เรียกใช้ได้เลย:
List<ProductSummary> summaries = productRepository.findByStatus("ACTIVE");
// ได้ List ของ Interface ที่เบาหวิว พร้อมใช้งานทันที
ความแตกต่างของผลลัพธ์ที่ได้
- Query ผอมลงทันตาเห็น: จาก
SELECT *กลายเป็นSELECT id, nameโดยอัตโนมัติ ลด Load ของ Database และ Network I/O มหาศาล - Zero Mapping Logic: คุณไม่ต้องเขียน Mapper หรือใช้ Library อย่าง MapStruct ให้วุ่นวาย Spring จัดการ Map เข้า Interface ให้เสร็จตั้งแต่ระดับ Query
- Nested Projection: มันทำได้ลึกกว่านั้น! ถ้าคุณอยากได้ชื่อ Category ของ Product ด้วย ก็แค่ใส่
CategorySummary getCategory();ใน Interface หลัก Spring จะ Join และดึงมาเฉพาะ field ที่กำหนดในCategorySummaryให้เอง
บทสรุป
อย่าปล่อยให้ความขี้เกียจที่ใช้ findAll() มาฆ่าแอพของคุณ เมื่อไหร่ที่ต้องการข้อมูลแค่ "บางส่วน" (ซึ่งคือ 80% ของ use cases) จงนึกถึง Projections ก่อนเสมอครับ เร็วขึ้น เบาขึ้น และเขียน Code น้อยลง
No responses yet. Be the first to respond.