المفاتيح الأساسية و @GeneratedValue
المفاتيح الأساسية و @GeneratedValue
لكل كيان JPA مفتاح أساسي — القيمة التي تُعرّف الصف بصورة فريدة في جدوله المُربوط. كيفية توليد ذلك المفتاح عند تنفيذ INSERT هي من أولى القرارات التي تتخذها عند ربط نموذج المجال، والاختيار الخاطئ يظهر لاحقًا على شكل اختناق في الأداء أو خطأ في التزامن. يغطّي هذا الدرس آليات @Id وكل استراتيجية متاحة ضمن @GeneratedValue مع المفاضلات العملية التي ينبغي أن تقود اختيارك.
الحد الأدنى: @Id
أي حقل ثابت مُعلَّق بـ @Id يصبح مفتاحًا أساسيًا. يمكنك وضع التعليق التوضيحي على الحقل نفسه (وصول الحقل) أو على الـ getter (وصول الخاصية)؛ يستخدم Hibernate الأسلوب الذي تختاره لجميع التعيينات الأخرى في ذلك الكيان. تُفضّل الممارسات الحديثة بشدة وصول الحقل.
بدون @GeneratedValue، يجب على التطبيق تعيين id قبل استدعاء persist(). هذا مقصود أحيانًا (المفاتيح الطبيعية، أو معرّفات UUID المُولَّدة في الكود)، لكن بالنسبة لمفاتيح الأعداد الصحيحة البديلة فهو غير عملي.
@GeneratedValue والاستراتيجيات الأربع
تعليق الحقل @Id بـ @GeneratedValue يُفوّض توليد المفتاح إلى مزوّد JPA. تقبل سمة strategy أربع قيم من تعداد GenerationType.
IDENTITY — دع عمود قاعدة البيانات يُولَّد تلقائيًا
GenerationType.IDENTITY يُعيَّن على عمود قاعدة البيانات من نوع AUTO_INCREMENT (MySQL/MariaDB) أو GENERATED ALWAYS AS IDENTITY (PostgreSQL 10+، SQL Server). تُخصّص قاعدة البيانات المفتاح عند الإدراج ويقرأه Hibernate فورًا.
DDL الذي يُولّده Spring Boot لهذا على MySQL يبدو كالتالي:
SEQUENCE — كائن تسلسل قاعدة البيانات
GenerationType.SEQUENCE يُفوّض إلى كائن تسلسل قاعدة بيانات مُسمَّى (مدعوم بشكل أصلي في PostgreSQL وOracle وH2 وSQL Server 2012+؛ غير متاح في MySQL قبل الإصدار 8). يستدعي Hibernate NEXT VALUE FOR sequence_name قبل INSERT، لذا يعرف المفتاح قبل كتابة الصف ويمكنه تجميع الإدراجات بحرية.
معامل allocationSize بالغ الأهمية للأداء. مع allocationSize = 50، يجلب Hibernate قيمة تسلسل واحدة من قاعدة البيانات ثم يزيد في الذاكرة لعمليات الإدراج الـ49 التالية قبل أن يتواصل مع قاعدة البيانات مجددًا. يجب أن يزيد تسلسل قاعدة البيانات بنفس المقدار (INCREMENT BY 50).
DDL المقابل:
AUTO — المزوّد يختار الاستراتيجية
GenerationType.AUTO هو الافتراضي عند حذف سمة strategy. يفحص Hibernate اللهجة ويختار ما يراه مناسبًا لقاعدة البيانات المستهدفة. على PostgreSQL يختار SEQUENCE؛ وعلى MySQL القديم يعود إلى تسلسل مشترك قائم على جدول TABLE.
TABLE — بديل للتنقلية
GenerationType.TABLE يُحاكي تسلسلًا باستخدام جدول قاعدة بيانات عادي. يعمل على كل قاعدة بيانات بما في ذلك MySQL، لكنه يتطلب قفلًا تشاؤميًا على مستوى الصف عند كل إدراج — SELECT … FOR UPDATE إضافية بالإضافة إلى UPDATE واحدة لكل طلب مفتاح. إنها الاستراتيجية الأبطأ ونادرًا ما تكون الاختيار الصحيح للمشاريع الجديدة.
استخدام UUID كمفتاح أساسي
يضيف Hibernate 6 وJPA 3.1 دعمًا أصليًا للـ UUID. صرّح عن الحقل كـ java.util.UUID واستخدم GenerationType.UUID (أو @UuidGenerator الخاص بـ Hibernate). يُولّد مزوّد JPA UUID قبل INSERT، مانحًا إياك نفس حرية التجميع كـ SEQUENCE.
تكون UUIDs مفيدة عندما يجب إنشاء كيانات عبر شظايا قاعدة بيانات متعددة أو نماذج خدمة بدون تسلسل مركزي. التكلفة هي صفحات فهرسة أكبر (16 بايت مقابل 8 بايت لـ BIGINT) وفهارس B-tree مُجزَّأة للغاية مع UUIDs العشوائية. بالنسبة للجداول كثيفة الكتابة على قواعد البيانات العلائقية، لا يزال BIGINT + SEQUENCE أسرع.
دليل اختيار الاستراتيجية
- PostgreSQL / Oracle / SQL Server (الحديث): SEQUENCE مع
allocationSizeمُضبوط. أفضل أداء، يدعم التجميع. - MySQL / MariaDB: IDENTITY هو الخيار العملي. اقبل أن إدراجات الدُفعات مُعطَّلة، أو استخدم SEQUENCE فقط إذا كنت على MySQL 8+ ومستعدًا لإنشاء كائنات التسلسل يدويًا (متاحة لكنها ليست الافتراضي في DDL).
- الأنظمة الموزعة / متعددة الشظايا: UUID. يُلغي الحاجة للتنسيق على حساب حجم الفهرس.
- الكود القديم المتنقّل: TABLE كملاذ أخير.
- مشاريع Spring Boot الجديدة (PostgreSQL): SEQUENCE +
allocationSize = 50هو الافتراضي الاصطلاحي.
المفاتيح المركّبة
يدعم JPA أيضًا المفاتيح الأساسية المركّبة عبر @IdClass أو @EmbeddedId. يُغطَّى هذا بالتفصيل في درس لاحق حول الكائنات المُدمجة (Embeddables). تجنّب المفاتيح المركّبة في الجداول الجديدة — المفاتيح البديلة الصحيحة أو UUID أبسط في التعامل وأفضل أداءً في عمليات الانضمام.
الخلاصة
يُعلّم @Id حقل المفتاح الأساسي؛ بينما يُفوّض @GeneratedValue تعيين المفتاح إلى المزوّد. IDENTITY هي الاستراتيجية الأبسط لكنها تحجب إدراجات الدُفعات. SEQUENCE هي الأكثر مرونة وأداءً — اضبط allocationSize لتقليل رحلات قاعدة البيانات. AUTO مريحة في العروض التجريبية لكنها غير متوقعة عبر إصدارات Hibernate؛ كن دائمًا صريحًا في الإنتاج. UUID تحل توليد المعرّف الموزع بتكلفة كفاءة الفهرسة. اختر بناءً على قاعدة بياناتك ونمط الكتابة، ثم تابع — بقية تعيين الكيان لا تهتم بالاستراتيجية التي اخترتها.