الاستعلامات الأصيلة والإسقاطات
الاستعلامات الأصيلة والإسقاطات
يغطي JPQL الغالبية العظمى من الاستعلامات اليومية، إلا أن هناك حالات تحتاج فيها إلى تجاوز طبقة التجريد وكتابة SQL خام: دوال خاصة بقاعدة البيانات، ودوال النوافذ، والتعبيرات الجدولية المشتركة المتكررة (CTEs)، أو الاستعلامات الحرجة الأداء التي لا تستطيع آلية توليد SQL في Hibernate مجاراتها. يجعل Spring Data JPA ذلك سهلًا من خلال @Query(nativeQuery = true). في الوقت ذاته، كثيرًا ما تحتاج فقط إلى شريحة من كيانك — بضعة أعمدة بدلًا من الرسم البياني الكامل للكائنات. تتيح لك الإسقاطات التعبير عن هذه الحاجة بشكل واضح مع فوائد أداء قابلة للقياس.
متى تستخدم الاستعلامات الأصيلة
قبل اللجوء إلى SQL الأصيل، اسأل نفسك: هل يستطيع JPQL أو الدوال المشتقة التعبير عن هذا؟ إن كانت الإجابة نعم، فضّل ذلك — فـ JPQL مستقل عن قاعدة البيانات ويعمل مع ذاكرة التخزين المؤقت الأولى في Hibernate. استخدم الاستعلامات الأصيلة عندما تحتاج إلى:
- صيغة خاصة بمورّد معين (
REGEXP_REPLACE،GENERATE_SERIES، دوال النوافذ مثلROW_NUMBER() OVER). - التعبيرات الجدولية المشتركة المتكررة (
WITH RECURSIVE). - عمليات
INSERT … SELECTأوMERGEالضخمة. - استدعاء الإجراءات المخزّنة التي لا يوجد لها ما يعادلها في JPQL.
- الحالات التي يكون فيها SQL المُولَّد من Hibernate أبطأ بشكل موثَّق من استعلام محسَّن يدويًا بعد التنميط.
entityManager.clear() أو أبطل منطقة التخزين المؤقت المتأثرة لتجنب القراءات القديمة.
كتابة استعلام أصيل باستخدام @Query
أضف nativeQuery = true إلى التعليق التوضيحي واكتب SQL كما تتوقعه قاعدة بياناتك تمامًا:
يعمل نوع الإرجاع List<Order> لأن الاستعلام يختار جميع الأعمدة (o.*) ويستطيع Hibernate تعيين مجموعة النتائج إلى كيان Order. إذا اختار SQL الخاص بك أعمدة محددة فقط أو استخدم أسماء مستعارة مختلفة، فإن تعيين الكيان يفشل — وهذه بالضبط المشكلة التي تحلها الإسقاطات.
الاستعلامات الأصيلة المسمّاة (موضع بديل)
لإعادة الاستخدام أو لإبقاء SQL خارج واجهات المستودعات، صرّح بالاستعلامات الأصيلة المسمّاة على فئة الكيان باستخدام @NamedNativeQuery:
@SqlResultSetMapping: يُعدّ @SqlResultSetMapping مطوَّلًا ومن حقبة XML. في الكود الجديد، إسقاطات واجهات Spring Data (المبيّنة أدناه) أكثر إيجازًا وبنفس الكفاءة.
الإسقاطات: المشكلة التي تحلّها
تخيّل كيان Order يحتوي على 20 عمودًا بما في ذلك حقل ملاحظات @Lob وعدة ارتباطات @ManyToOne. نقطة نهاية واجهة المستخدم التي تحتاج فقط إلى id وstatus وtotalAmount لعرض قائمة لا ينبغي لها جلب كل ذلك. تتيح لك الإسقاطات تحديد الأعمدة التي تريدها بالضبط، ويُولّد Hibernate استعلام SELECT يجلب تلك الأعمدة فقط.
إسقاطات الواجهات (المغلقة)
عرّف واجهة Java بأساليب getter تتطابق مع أسماء خصائص الكيان. يُولّد Spring Data وكيلًا (proxy) في وقت التشغيل:
عند استخدام @Query مع إسقاط، أضف اسمًا مستعارًا لكل تعبير محدد يطابق اسم الخاصية من واجهة الإسقاط بالضبط (AS id، AS totalAmount). يُطابق Hibernate الأعمدة بأساليب getter عن طريق الاسم المستعار.
إسقاطات الواجهات (المفتوحة) — تعبيرات SpEL
يمكن للإسقاطات المفتوحة دمج القيم أو حسابها باستخدام لغة تعبير Spring:
إسقاطات DTO (المستندة إلى الفئات)
إذا أردت DTO محددًا وغير قابل للتغيير بدلًا من وكيل، استخدم سجل Java (record) أو فئة مع تعبير بنّاء JPQL:
يستدعي تعبير new في JPQL بنّاء DTO مباشرةً. يختار الاستعلام الأعمدة الثلاثة المعيَّنة فقط — دون تكلفة وكيل، ودون استدعاء انعكاس لكل getter. هذا هو الخيار الأفضل عندما يُسلسَل DTO إلى JSON في استجابة REST، لأن Jackson يعمل بشكل أفضل مع الأنواع الملموسة من وكلاء Hibernate.
الإسقاطات مع الاستعلامات الأصيلة
تعمل إسقاطات الواجهات أيضًا مع nativeQuery = true. أضف أسماء مستعارة لأعمدة SQL لتتطابق مع أسماء getters في الإسقاط:
null، تحقق من أن الاسم المستعار في SQL يطابق اسم getter بالضبط (مطابقة حساسة لحالة الأحرف مقابل اسم getter بنمط camelCase مع إزالة "get").
مقارنة مفاضلات الأداء
- جلب الكيان الكامل: تُحمَّل جميع الأعمدة؛ يُتتبَّع الكيان بواسطة سياق المثابرة؛ مثالي لعمليات التحديث والحذف.
- إسقاط الواجهة المغلقة: تُجلب فقط الأعمدة المحددة من قاعدة البيانات؛ الوكيل يُغلّف مجموعة — تكلفة CPU طفيفة لكل استدعاء getter، لكن مكسب كبير في عرض النطاق الترددي على الكيانات العريضة.
- إسقاط DTO (سجل/فئة): تُجلب الأعمدة المحددة فقط؛ تعيين بالبنّاء؛ لا وكيل؛ لا تتبع بسياق المثابرة. الأفضل لاستجابات API للقراءة فقط.
- إسقاط الواجهة المفتوحة: يُحمَّل الكيان الكامل داخليًا؛ يُقيَّم SpEL؛ لا ميزة تقليل أعمدة — استخدمه فقط عند الحاجة إلى خصائص محسوبة.
الخلاصة
تمنحك الاستعلامات الأصيلة مخرجًا إلى SQL الخام عندما لا يستطيع JPQL التعبير عما تحتاجه، مع البقاء متكاملًا تمامًا مع نموذج مستودع Spring Data. تُحدّد الإسقاطات — سواء كانت مستندة إلى واجهات أو DTOs — استعلاماتك لاسترداد البيانات التي يستهلكها المُستدعي فعلًا فقط، مما يقلل من إدخال/إخراج قاعدة البيانات وتخصيص الكائنات. ادمجهما: استعلام تجميع أصيل يُعيد إسقاط واجهة هو نمط نظيف وعالي الأداء لنقاط نهاية التقارير. في الدرس القادم ستضيف التصفح الصفحي والترتيب إلى هذه الاستعلامات، مما يتيح نقاط نهاية قوائم فعّالة بغض النظر عن حجم مجموعة البيانات.