20 คะแนน โดย carnoxen 2024-12-05 | 22 ความคิดเห็น | แชร์ทาง WhatsApp

สรุป

JPA/Hibernate กลายเป็นเฟรมเวิร์กที่ถูกใช้อย่างแพร่หลาย เพราะทำให้ดูเหมือนไม่จำเป็นต้องเขียน SQL ในโค้ด Java อีกต่อไป แต่ผมอยากยืนยันว่าไม่ควรใช้มันในโปรเจ็กต์ใหม่

เหตุผล

เอกสารทางการที่ยาวมาก

เมื่อนำเอกสารทางการไปแปลงเป็น PDF จะมีความยาวถึง 406 หน้า ซึ่งมากกว่า The Lord of the Rings (231 หน้า) และเอกสารมาตรฐาน SQL (288 หน้า) เสียอีก คุณไม่จำเป็นต้องเรียนระดับปริญญาโทเพื่อทำความเข้าใจการคิวรีฐานข้อมูล

ความสามารถในการเปลี่ยนแปลงสถานะ

  1. ต่อให้เอนทิตีต้องการองค์ประกอบบางอย่าง ก็ยังถูกบังคับให้มี constructor ที่ไม่รับอาร์กิวเมนต์
  2. คุณไม่สามารถป้องกันการสืบทอดได้ด้วยการใส่คีย์เวิร์ด final, abstract ให้กับคลาสเอนทิตี
  3. Reflection/Introspection เพิกเฉยต่อหลัก encapsulation ของ OOP
  4. ใครบางคนสามารถฝังโค้ดประสงค์ร้ายเพื่อทำลายข้อมูลทั้งหมดได้

Lazy loading และแคช

  1. แอโนเทชัน @Lazy เป็นเทคนิคที่แย่ที่สุดสำหรับมือใหม่ แต่ก็หลีกเลี่ยงไม่ได้เมื่อการออกแบบโดเมนไม่เข้ากับ Hibernate หรือเมื่อไม่สามารถเขียนคิวรีได้
  2. กลไกแคชเข้าใจได้ยาก ยิ่งไปกว่านั้น ต่อให้เข้าใจแล้ว ก็ยังต้องเก็บตัวเอนทิตีไว้ในแคชแทนที่จะเก็บผลลัพธ์ของคิวรี

การซิงก์หน่วยความจำกับฐานข้อมูล (Flush)

เทคนิคที่เรียกว่า Flush ใช้ซิงก์ออบเจ็กต์ที่เก็บอยู่ในหน่วยความจำกับฐานข้อมูล ซึ่งทำให้เกิดสองปัญหา

  • เมื่อ Flush ทำงาน การแก้ไขในหน่วยความจำจะถือว่าสิ้นสุดลง ทำให้แทบไม่มีทางใช้ persistence tool อื่นนอกเหนือจาก Hibernate ได้ และ
  • หากเกิดการชนกันระหว่างการ Flush อาจมี Stack Trace ที่ไม่เกี่ยวกับโค้ดโผล่ขึ้นมาเป็นข้อผิดพลาดได้

การดึงเฉพาะบางคอลัมน์จากตารางเดียว

ถ้าต้องการเข้าถึงเพียงคอลัมน์เดียวของเอนทิตี แนวทางของ SQL นั้นง่ายมากดังนี้

select url from image   
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';  

แต่ Hibernate จะดึงทุกคอลัมน์ของเอนทิตีเสมอ หากต้องการหลีกเลี่ยงก็ต้องผ่านกระบวนการที่ซับซ้อน

การกำหนด constraint ให้คอลัมน์

หากต้องการกำหนดข้อจำกัดให้กับคอลัมน์หนึ่งคอลัมน์ ต้องใส่หลายแอโนเทชันแบบด้านล่าง

...  
    @NotNull  
    @NotEmpty  
    @Email  
    private String email;  
...  

วิธีนี้ก่อให้เกิดปัญหาต่อไปนี้

  • ไม่สามารถเขียน unit test สำหรับเงื่อนไขเหล่านี้ได้
  • พอจะมองเห็นกระบวนการนี้ระหว่างทำ Flush ก็สายเกินไปแล้ว
  • ข้อยกเว้นที่เกิดขึ้นได้มีลักษณะทั่วไปและแทบไม่มีประโยชน์
  • มันปฏิบัติต่อกฎทางธุรกิจเหมือนเป็นเพียงกฎทางเทคนิคเท่านั้น

ปัญหาในเชิงกลยุทธ์

  1. การอัปเดตของเฟรมเวิร์กแย่มาก มักไม่สนใจ backward compatibility และทำให้เกิดการพึ่งพาแบบหลีกเลี่ยงไม่ได้ บริษัทที่สร้างมันยังทำให้คนคิดว่าการใช้เฟรมเวิร์กของตัวเองเพื่อผูกขาดเป็นเรื่องปกติ วงจรอุบาทว์นี้ควรถูกหยุด
  2. การได้มาซึ่งProof of Conceptผ่านเฟรมเวิร์กเท่านั้น จะยิ่งทำให้มุมมองของตัวเองแคบลง และในกรณีของ JPA/Hibernate ยิ่งเป็นเช่นนั้นมากขึ้นไปอีก อย่ายอมให้มีแม้แต่น้อย

แล้วควรทำอย่างไร?

ใช้ SQL

ทุกอย่างทำได้ด้วย SQL เพียงอย่างเดียว โปรแกรมเมอร์ทุกคนรู้จัก คิวรีเข้าใจง่าย และไม่ต้องพึ่งเฟรมเวิร์ก

ถ้าผู้จัดการบอกให้ใช้ Hibernate ล่ะ?

ลาออกเสีย หรือไม่ก็แยกโค้ดออกจากเฟรมเวิร์กให้ได้

ถ้าใช้อยู่แล้ว...

  1. อย่าทำให้ constructor เริ่มต้นและ setter เป็น public
  2. ใช้สตริงอย่าง UUID แทนไอดีที่สร้างโดย SQL
  3. ใช้ชื่อ XXXDao แทน XXXRepository
  4. อย่าใช้แอโนเทชัน @SequenceGenerator
  5. แยกโดเมนคลาสและคลาส DAO ออกเป็น interface
  6. อย่าใช้ความสัมพันธ์แบบหลายรายการ (@OneToMany เป็นต้น) และถ้าเป็นไปได้ก็ควรหลีกเลี่ยง entity mapping ไปเลย

บทสรุป

JPA/Hibernate เลิกใช้เสีย

  • มีเอกสารที่สั้นกว่าและดีกว่าสำหรับแก้ปัญหาทางธุรกิจ
  • อย่ายึดติดกับแนวทางการออกแบบที่เร็วเกินไปเพียงแบบเดียว
  • จงมีเมตตาต่อนักพัฒนาคนถัดไปที่จะต้องมาดูแลโค้ดของคุณ

พวกคุณใช้อะไรกันอยู่?

  1. JPA/Hibernate
  2. เทคโนโลยี ORM อื่น

22 ความคิดเห็น

 
askaskm 2024-12-16

ฉันไม่เห็นด้วยกับความเห็นที่บอกว่าควรเลิกใช้ JPA/Hibernate

"เอกสารทางการที่ยาวมาก"
แม้แต่ SQL เองตอนเริ่มเรียนก็ยากเหมือนกัน ไม่ใช่ว่าจะเข้าใจ join ที่ซับซ้อน, subquery, ฟังก์ชันของ procedure ต่าง ๆ ได้อย่างสมบูรณ์แบบกันง่าย ๆ ใช่ไหม?
JPA แค่เริ่มต้นโดยเข้าใจแนวคิดหลักก่อนก็เพียงพอแล้ว ส่วนเรื่องที่ลึกกว่านั้นค่อยไปค้นหาตอนที่จำเป็นก็ได้
และยังมี LLM อยู่ด้วย

"ปัญหาเรื่องความไม่แน่นอนและ Reflection"
นี่เป็นความกังวลที่เกิดจากการไม่เข้าใจวิธีการทำงานของเฟรมเวิร์ก
ในงานจริง แทบจะไม่มีกรณีที่เกิดปัญหาอย่างเป็นรูปธรรมจากเรื่องนี้
ตรงกันข้าม การทำ object mapping แบบอัตโนมัติด้วย Reflection ช่วยเพิ่มประสิทธิภาพการพัฒนาได้อย่างมาก

"lazy loading และ cache"
บอกว่า @Lazy เป็น "เทคโนโลยีที่แย่ที่สุด" งั้นหรือ? มันเป็นฟีเจอร์ที่มีประโยชน์มากในการแก้ปัญหา N+1 และปรับแต่งประสิทธิภาพ
กลไก cache กลับยิ่งช่วยเพิ่มประสิทธิภาพได้มากด้วยซ้ำ

"การดึงเฉพาะคอลัมน์บางตัวจากตารางหนึ่ง"
ถ้าใช้ JPQL หรือ Projection ก็สามารถดึงเฉพาะคอลัมน์ที่ต้องการได้อย่างง่ายดาย
และใช้ร่วมกับ QueryDSL ก็ได้

ผมคิดว่าเป้าหมายของ ORM ไม่ใช่การแทนที่ SQL อย่างสมบูรณ์ แต่คือการช่วยให้นักพัฒนาสามารถโฟกัสกับ business logic ได้มากขึ้น..

 
bbulbum 2024-12-09

ผมค่อนข้างเป็นพวกมองโลกในแง่ร้ายกับ ORM แต่ก็ดูเหมือนว่าจะยังเสนอทางเลือกที่ดีพอได้ไม่มากนัก

ถ้าเดินไปในทางที่พึ่ง ORM หนัก ๆ มันก็ไม่มีที่สิ้นสุดจริง ๆ และอย่างที่กล่าวไว้ข้างต้น คุณอาจต้องดิ้นรนอยู่ในเอกสารที่มีขอบเขตกว้างกว่าเอกสาร SQL เสียอีก จนสุดท้ายอาจแห้งตายคากองเอกสารก็ได้

ช่วงนี้ผมกำลังพัฒนาโปรเจกต์ส่วนตัวโดยไม่ใช้ ORM แต่พอออกแบบโดยคำนึงถึงการนำกลับมาใช้ซ้ำไปเรื่อย ๆ ก็มีหลายครั้งเหมือนกันที่รู้สึกว่าท้ายที่สุดตัวเองกำลังพัฒนาไปในทิศทางที่เหมือนกำลังสร้าง ORM ขึ้นมาเองอยู่ดีครับ 555

 
ilbanin00 2024-12-07

ดูเหมือนว่าในบทความที่บอกว่าไม่ควรใช้อะไรทำนองนี้ มักจะมองข้ามข้อดีของการใช้เฟรมเวิร์กไปเสมอ นั่นคือเราสามารถยึดถือพาราไดม์ร่วมกันกับนักพัฒนาคนอื่น ๆ ที่ใช้เฟรมเวิร์กเดียวกันได้ เพราะเราใช้เฟรมเวิร์กนั้น

 
dothx 2024-12-06

ถ้ามีตารางเยอะและคอลัมน์ก็เยอะด้วย (เช่น มี 50 ตาราง และแต่ละตารางมีคอลัมน์มากกว่า 100 คอลัมน์) พอเขียน SQL ตรง ๆ แล้วมันเหมือนนรกชัด ๆ เลยครับ

แต่ถ้าจะทำบริการขนาดเล็ก ผมคิดว่าการใช้ JPA/Hibernate เป็นอะไรที่สิ้นเปลืองมาก

สุดท้ายความเห็นแบบนี้ก็คงแล้วแต่กรณีจริง ๆ ครับ

(แม้แต่ตัวอย่างที่ยกมาก็ยังเป็นแบบมีคอลัมน์แค่ 3~4 คอลัมน์...)

 
jpumpkin94 2024-12-06

ดูเหมือนว่าคำถามสุดท้ายในบทความข้างต้นน่าจะต้องปรับแก้นิดหน่อย

ในฝั่ง Java อาจสรุปได้เป็น 1. ORM vs 2. Non-ORM

  1. ถ้าเป็น ORM ในทางปฏิบัติก็แทบจะหมายถึงการใช้ชุด JPA/Hibernate เท่านั้น
  2. ก็มี MyBatis, JOOQ, SpringDataJDBC เป็นต้น ซึ่งโดยหลักแล้วจะเป็นการจัดการ SQL โดยตรง

ทั้งข้อ 1 และ 2 ต่างก็มีข้อดีข้อเสียที่ชัดเจน ดังนั้นการสรุปแบบสุดโต่งเหมือนในบทความข้างต้นจึงไม่เหมาะสมนัก

ในกรณีของพวกเรา
เราใช้ทั้ง JPA/Hibernate/QueryDSL ซึ่งเป็น ORM ควบคู่ไปกับ MyBatis

เราใช้ ORM เพื่อเพิ่ม productivity ให้ได้มากที่สุด
และสำหรับ query ที่ ORM ครอบคลุมได้ยาก ก็จะใช้ MyBatis

และไม่ว่าจะเลือกข้อ 1 หรือ 2 ข้างต้น อย่างไรเสียก็ต้องรู้ SQL ให้ดี

 
carnoxen 2024-12-06

ผมก็อยากแก้ไขเหมือนกัน แต่ในเว็บไม่มีฟีเจอร์แบบนั้นน่ะ...

 
kallare 2024-12-06

ดูเหมือนจะกำลังแกล้งไม่รู้เหตุผลที่ทำให้ ORM ได้รับความนิยมตั้งแต่แรกนะครับ

แม้จะมีต้นทุนในการเรียนรู้อยู่บ้าง แต่พอคุ้นเคยแล้ว ประสิทธิภาพในการพัฒนาก็ดีขึ้นอย่างชัดเจน

SQL ดูเหมือนจะง่ายก็จริง แต่ความเหนื่อยเวลาต้องเขียน SQL ทีละบรรทัดนี่... แถมพอตารางเปลี่ยน ก็ต้องตามแก้ query ที่เกี่ยวข้องทั้งหมดทีละจุดอีก การดูแลรักษา SQL เลยไม่ใช่งานง่ายเลย และยิ่งระบบเล็กกับเรียบง่ายเท่าไร ปริมาณงานจุกจิกก็ยิ่งเยอะขึ้น (ก็เลยมีเรื่อง productivity ถูกพูดถึงตามมาเรื่อย ๆ)

อีกอย่าง error ที่เกิดจาก SQL มักจะระเบิดตอน runtime ทำให้จับยาก และถ้าต้องคอยป้องกันการโจมตีอย่าง SQL injection ทีละจุดไปเรื่อย ๆ สุดท้ายก็มักต้องเพิ่มโค้ดสำหรับสร้าง query ขึ้นมาเอง (ปกติก็เริ่มจากรูปแบบ template ง่าย ๆ ก่อน..) ทำไปทำมาก็สุดท้ายจะได้ของที่คล้าย ORM กลับมาอยู่ดี งั้นใช้ ORM ไปเลยไม่ดีกว่าเหรอ..?

ทำให้นึกถึงโพสต์ที่ขึ้นมาเมื่อไม่กี่วันก่อนนะครับ
https://th.news.hada.io/topic?id=17955

 
laracool 2024-12-06

เห็นด้วยครับ
ดูเหมือนว่าหลายครั้งผู้คนอาจยังเข้าใจไม่มากพอว่าทำไมถึงใช้ ORM และมันมีข้อดีอะไรบ้าง

เพิ่มเติมคือ ดูเหมือนจะมีคนไม่มากนักที่พยายามวิเคราะห์หรือทำความเข้าใจ SQL ที่ถูกรันจริงผ่าน ORM
ผมอยากให้มีการรับรู้มากขึ้นว่า มันไม่ได้ให้แค่ความสะดวกเท่านั้น แต่ยังช่วยให้เข้าใจการปรับแต่ง SQL ให้เหมาะสมและการทำงานของฐานข้อมูลได้ลึกซึ้งขึ้นด้วย

 
jamsya 2024-12-06

ผมคิดว่าไม่จำเป็นต้องสุดโต่งไปทางใดทางหนึ่งว่าจะต้องใช้หรือห้ามใช้เลยครับ 555;;
ถ้าต้องการเรื่องประสิทธิภาพการพัฒนา ผมก็จะใช้ ORM
แต่สำหรับคิวรีที่ซับซ้อนซึ่ง ORM ครอบคลุมไม่ได้ หรือคิวรีที่ต้องการการปรับจูนเพิ่มเติม ก็จะจัดการด้วย raw query
ผมคิดว่าจะเลือกใช้ ORM หรือ raw query ก็ควรเลือกให้เหมาะสมตามสถานการณ์ ว่าจะสร้างอะไรและอย่างไร

 
xhfleodhkd 2024-12-06

โดยทั่วไป ถ้าฐานข้อมูลมีการทำ normalization ไว้ดีอยู่แล้ว และเป็นข้อมูลที่ไม่จำเป็นต้อง join มากนัก ผมก็มองว่าเป็นแบบนั้นได้
แต่ถ้าตั้งแต่การทำ normalization ของฐานข้อมูลไปจนถึงการจัดการทุกอย่าง ไม่สามารถดูแลผ่าน DBA ได้อย่างเหมาะสม ผมก็คิดว่า ORM ก็อาจเป็นตัวเลือกที่ดีได้เช่นกัน โดยเฉพาะข้อดีที่เกิดขึ้นจากการดึงข้อมูลฝั่งที่ปกติต้องเอามาผ่าน join แต่เปลี่ยนมาเอาผ่าน relationship นั้น ผมคิดว่าเป็นตัวอย่างที่ดีมากว่าทำไมคนถึงเลือกใช้ ORM
แน่นอนว่า ผมเห็นด้วยกับความเห็นที่ว่าการพึ่งพา framework อาจจำกัดการเติบโตของนักพัฒนา และเราควรลดการพึ่งพา framework ลง
แต่กับความเห็นที่ว่าห้ามใช้ ORM แบบเหมารวมไปเลย ผมคงเห็นด้วยได้ไม่ง่ายนัก
มันเหมือนตั้งต้นจากสมมติฐานว่าทุกบริษัทมี DBA กันหมด และพัฒนาด้วยแนวทางที่เป็นระบบจริง ๆ อย่าง DDD หรือ TDD
ถ้าในงานจริงต้องทำกันแบบนั้น ผมไม่รู้เลยว่าโค้ดจะเละเทะกว่านี้ไปได้อีกแค่ไหน

 
aer0700 2024-12-06

เวลาสร้างแบ็กเอนด์ด้วย PYTHON ก็มักจะใช้ประมาณ SQLALCHEMY หรือ DJANGO ORM กันเป็นประจำ
พอคุ้นเคยแล้วก็รู้สึกว่าไม่ค่อยต่างกันมากนักระหว่างการเขียน SQL โดยตรงกับการใช้ ORM เลยไม่ได้คิดอะไรมาก
แต่ก็มีความเห็นเหมือนกันว่าไม่ควรใช้ ORM นะครับ เห็นด้วยกับความเห็นที่ว่าควรลดการพึ่งพาเฟรมเวิร์ก อย่างน้อยก็ไม่ควรเป็นแบบที่ใช้ได้แค่ DJANGO ORM แต่จัดการ SQL ไม่เป็น...

 
beoks 2024-12-06

อืม ผม/ฉันคิดต่างนะครับ/ค่ะ ตอนนี้ผม/ฉันกำลังดูแลบริการที่มีตารางอยู่ราว 3,000 ตาราง และเพราะโดเมนซับซ้อนมาก แค่จะเขียนคิวรีหนึ่งอันก็โดยพื้นฐานแล้วเกินหลายสิบบรรทัดไปแล้ว พอคิดถึงคิวรีแบบไดนามิกเข้าไปด้วยยิ่งปวดหัวจริง ๆ ครับ/ค่ะ เพราะมันซับซ้อนเลยเกิดบั๊กได้เยอะ และดูแลรักษาก็ยากด้วย สำหรับผม/ฉัน ผม/ฉันคิดว่าในโดเมนที่ซับซ้อน ORM มีข้อได้เปรียบมากกว่า

 
xhfleodhkd 2024-12-06

ในกรณีของผมเคยมีประสบการณ์ดูแลรักษา db ที่ไม่ได้ทำ normalization
ตอนนั้นเวลาเขียน dynamic query โดยไม่ใช้ orm แล้วใช้ SQL ปกติแทน
พอเป็นแบบนั้น บางครั้งโค้ดก็ยิ่งอ่านและทำความเข้าใจได้ยากขึ้น
ผมมองว่าไม่ใช่แค่โดเมนที่ซับซ้อนเท่านั้น แต่โดเมนที่มี normalization ไม่เพียงพอก็มีพื้นที่ให้พิจารณานำมาใช้ได้มากพอเช่นกัน

 
carnoxen 2024-12-06

อ้อ งั้นก็คงไม่จำเป็นต้องมองในแง่ร้ายนะ

 
tsboard 2024-12-06

โดยส่วนตัวแล้วผมก็อยากแนะนำให้ใช้ SQL ตรง ๆ มากกว่าครับ ในโลกของ JS มักใช้เครื่องมืออย่าง Prisma กันเยอะ แต่ SQL ก็ไม่ใช่ภาษาที่เข้าใจยากขนาดนั้นอยู่แล้ว และผมก็รู้สึกไม่ค่อยอยากใช้เท่าไร เพราะมันเหมือนต้องมีการทำ abstraction ที่ไม่จำเป็นมากเกินไปสำหรับ database I/O

 
znjadong 2024-12-06

ผมก็รู้สึกว่าอาจเป็นเพราะฝั่ง js/ts มี ORM หลายตัวที่คุณภาพค่อนข้างหลวม ๆ อยู่เยอะเป็นพิเศษ

 
carnoxen 2024-12-06

ถ้าเป็นอะไรอย่าง Jdbc ก็น่าจะพอใช่ไหม? พอนึกถึงที่เมื่อก่อนมีคนที่เคยทำงานกับผมพูดว่า "JPA ช้า เลยให้ใช้อย่างอื่น" ขึ้นมาเลยครับ

 
roxie 2024-12-06

ฟังดูเหมือนเรื่องเล่าในตำนานเลย

 
caniel 2024-12-06

ดูเหมือนว่าแนวโน้มกำลังกลับไปสู่พื้นฐานมากกว่าการพึ่งเฟรมเวิร์กนะ
ทั้ง HTMX, SQL และอื่น ๆ..

 
carnoxen 2024-12-06

แม้จะมีข้อเสียคือต้องสร้างวงล้อขึ้นมาใหม่ก็ตาม

 
misolab 2024-12-06

ก็มีข้อดีอยู่เหมือนกันนะ..
2. MyBatis (ถึงจะไม่ใช่ ORM ก็ตาม ฮ่า)

 
carnoxen 2024-12-06

น่าจะเปลี่ยนไปใช้ DAO แทน ไม่ใช่ ORM ตั้งแต่แรกก็คงดี