تعيين الكيان (Entity)
تعيين الكيان (Entity)
في JPA وHibernate، تتحوّل فئة Java العادية إلى كيان دائم (persistent entity) فور تزيينها بالتعليقات التوضيحية الصحيحة. تؤدي ثلاثة تعليقات توضيحية معظم العمل: @Entity يُخبر مزوّد الاستمرارية (persistence provider) أن الفئة تشارك في نمط ORM، و@Table يتحكّم في كيفية تعيين تلك الفئة على جدول في قاعدة البيانات، و@Column يضبط بدقة كيفية تعيين كل حقل على عمود. يغطي هذا الدرس الثلاثة بعمق — الإعدادات الافتراضية والخيارات والمقايضات التي تهمّ في كود الإنتاج.
@Entity — تسجيل فئة مع JPA
وضع @Entity على فئة هو الإعلان الأدنى بأنّها مُدارة من قِبل سياق الاستمرارية. سيشملها Hibernate في توليد المخطط والمسح واستعلامات JPQL.
بشكل افتراضي، يُعيّن JPA هذه الفئة على جدول اسمه مساوٍ لاسم الفئة غير المؤهَّل — Product تُعيَّن على جدول Product (أو product في محركات غير حساسة لحالة الأحرف كـ MySQL). هذا مقبول للنماذج الأولية السريعة، لكن في بيئة الفريق أو المؤسسة ستحتاج دائمًا تقريبًا إلى تحكّم صريح.
protected بدلًا من public للإشارة إلى أن كود التطبيق لا ينبغي أن يستدعيه مباشرةً — بينما يستطيع مزوّد الاستمرارية الوصول إليه.
@Table — التحكّم في اسم الجدول والقيود
يُوضع @Table على مستوى الفئة جنبًا إلى جنب مع @Entity. أهم سماته هي name التي تحدد اسم جدول SQL الدقيق.
السمات الرئيسية لـ @Table:
- name — اسم الجدول الدقيق في SQL. اضبطه دائمًا بشكل صريح في كود الإنتاج؛ لا تعتمد على الاسم الافتراضي المستنتج من اسم الفئة.
- schema — مخطط قاعدة البيانات أو الفضاء. مفيد في عمليات النشر متعددة المخططات (مثل
catalogوordersوauthكمخططات منفصلة في قاعدة البيانات نفسها). - catalog — اسم كتالوج قاعدة البيانات (أقل شيوعًا؛ وثيق الصلة ببعض قواعد البيانات كـ SQL Server).
- uniqueConstraints — قيود الفرادة على مستوى DDL التي يولّدها Hibernate أثناء توليد المخطط. هذه مجرد تلميحات إعلانية لأدوات توليد المخطط — لا تفرض أي شيء على مستوى JPA في وقت التشغيل.
- indexes — فهارس إضافية تُصدَر أثناء توليد المخطط. مرةً أخرى، DDL فقط؛ Hibernate لا يستخدمها في وقت الاستعلام.
snake_case لأسماء الجداول. اعلن دائمًا @Table(name = "orders") بشكل صريح بدلًا من تركه للصدفة. يمنع هذا أيضًا المفاجآت عند الانتقال بين نظام ملفات حساس لحالة الأحرف (Linux) ونظام غير حساس (macOS وWindows).
@Column — تعيين الحقول على الأعمدة
بدون أي تعليق توضيحي، تعيّن JPA كل حقل غير ساكن وغير مؤقت على عمود اسمه مطابق لاسم الحقل. يتيح لك @Column تجاوز كل جانب من جوانب هذا التعيين.
أهم سمات @Column:
- name — اسم عمود SQL. اتبع نفس اتفاقية snake_case كأسماء الجداول.
- nullable — عند ضبطه على
false، يضيف Hibernate قيدNOT NULLأثناء توليد المخطط. ويُمكّن أيضًا تكامل Bean Validation عندما تكون واجهةjakarta.validationAPI موجودة في classpath. - unique — يضيف قيد فرادة لعمود واحد. للقيود المتعددة الأعمدة استخدم
@Table(uniqueConstraints = ...)بدلًا من ذلك. - length — ينطبق على أعمدة
VARCHAR(حقول String). الافتراضي 255 — اضبط دائمًا طولًا صريحًا للأعمدة النصية المُفهرسة أو التي تعرف حدّها الأقصى. - precision وscale — تُستخدمان لأعمدة
DECIMAL/NUMERIC(حقول BigDecimal).precision= إجمالي الأرقام المعنوية؛scale= الأرقام بعد الفاصلة العشرية. - updatable — عند ضبطه على
false، يحذف Hibernate هذا العمود من جمل SQLUPDATE. استخدمه لحقول التدقيق كـcreated_atالتي يجب ألا تتغير بعد الإدراج. - insertable — عند ضبطه على
false، يحذف Hibernate العمود من جمل SQLINSERT. مفيد للأعمدة التي تولّدها قاعدة البيانات (مثل عمود محسوب أو قيمة افتراضية من trigger). - columnDefinition — مقطع DDL خام يتجاوز تعريف العمود بالكامل في توليد المخطط. استخدمه باعتدال؛ فهو يربط كودك بـ dialect قاعدة بيانات محدّد.
nullable = false يخبر Hibernate بإصدار NOT NULL عند توليد المخطط وتمكين قيود Bean Validation، لكنه لا يرمي استثناء JPA إذا حاولت حفظ كيان بقيمة null. ستُصدر قاعدة البيانات خطأ انتهاك قيد عند الـ flush — يظهر كـ ConstraintViolationException داخل DataIntegrityViolationException في Spring. اقرنه دائمًا بـ @NotNull من Bean Validation (jakarta.validation.constraints.NotNull) لاكتشاف المشكلة في طبقة الخدمة قبل الوصول إلى قاعدة البيانات.
الجمع معًا: كيان واقعي متكامل
إليك كيانًا كاملًا وجاهزًا للإنتاج يستخدم التعليقات الثلاثة بتناسق:
نوع الوصول: الحقل مقابل الخاصية
تحدّد JPA مكان قراءة القيم وكتابتها بناءً على مكان وضع تعليق @Id. إذا كان @Id على حقل، تصل Hibernate إلى جميع الحقول مباشرةً عبر الانعكاس (وصول الحقل). وإذا كان @Id على دالة getter، تصل Hibernate إلى كل الحالة المستمرة من خلال دوال getter وsetter (وصول الخاصية).
وصول الحقل هو الخيار المعاصر المفضّل بشدة. فهو يُبقي تعليقات التعيين متجاورة مع الحالة التي تصفها، ويتجنّب الحاجة إلى دوال getter/setter صنعية لـ JPA فحسب، ويعمل بشكل نظيف مع records في الإصدارات المستقبلية. لا تخلط بين وصول الحقل والخاصية في هرمية الكيان نفسه بدون تعليق @Access صريح — فالسلوك في هذه الحالة غير محدّد.
الخلاصة
تشكّل ثلاثة تعليقات توضيحية هيكل كل كيان JPA. @Entity يسجّل الفئة لدى مزوّد الاستمرارية. @Table يتحكّم في اسم جدول SQL والمخطط وقيود الفرادة والفهارس. @Column يضبط بدقة اسم عمود كل حقل وقابليته للقيمة الفارغة وطوله ودقته وقابليته للتعديل. كن صريحًا دائمًا — الاعتماد على الإعدادات الافتراضية للتسمية يُفضي إلى مفاجآت مع نمو قاعدة الكود أو تغيّر dialect قاعدة البيانات. اقرن nullable = false بتعليقات Bean Validation لاكتشاف الانتهاكات قبل وصولها إلى قاعدة البيانات. في الدرس القادم ستتعرّف على كيفية تحكّم @Id و@GeneratedValue في استراتيجيات توليد المفتاح الأساسي.