الكيانات وأساسيات @Entity
الكيانات وأساسيات @Entity
في Spring Data JPA، الكيان الدائم (persistent entity) هو فئة Java عادية يُعيّنها Hibernate إلى جدول في قاعدة بيانات علائقية. يُعلَن عن هذا التعيين بالكامل عبر التعليقات التوضيحية (annotations) — دون XML ودون خطوة توليد كود. يسير هذا الدرس عبر كل ما تحتاجه لإنشاء كيان صحيح وجاهز للإنتاج: التعليقات المطلوبة وقواعد تسمية الأعمدة واستراتيجيات الهوية ومقايضات الأداء التي عليك فهمها قبل كتابة أول استدعاء لـ save().
الكيان الأدنى القابل للتطبيق
تعليقان توضيحيان إلزاميان لكل كيان: @Entity يُعلّم الفئة كنوع تديره JPA، و@Id يُحدّد الحقل الذي يُعيَّن إلى عمود المفتاح الأساسي. يستخدم Spring Boot 3 الحزمة jakarta.persistence (Jakarta EE 9 فأعلى)، وليس الحزمة القديمة javax.persistence.
protected (لا public) لتثبيط كود التطبيق من استدعائه مباشرةً.
استنتاج اسمَي الجدول والعمود
يشتق Hibernate 6 افتراضيًا اسم الجدول من اسم الفئة واسم العمود من اسم كل حقل، وفق ImplicitNamingStrategy المُهيَّأة. يضبط Spring Boot هذه الاستراتيجية إلى SpringImplicitNamingStrategy التي تحوّل camelCase إلى snake_case: الحقل unitPrice يُصبح العمود unit_price.
استخدم @Table و@Column عندما تحتاج إلى تجاوز الإعدادات الافتراضية أو تقييد المخطط (schema):
nullable = false على الأعمدة غير الاختيارية. يستطيع Hibernate توليد DDL (spring.jpa.hibernate.ddl-auto=validate في بيئة CI) وسيُنبّه إلى قيود NOT NULL المفقودة. هذا يكتشف انجراف المخطط مبكرًا بدلًا من وقت التشغيل.
استراتيجيات توليد المفتاح الأساسي
اختيار استراتيجية @GeneratedValue الصحيحة يؤثر على معدل إدخال البيانات وقابلية النقل وكيفية تجميع الإدراجات على دفعات. تُعرِّف JPA أربع استراتيجيات عبر مُعدَّد GenerationType:
- AUTO (افتراضي): يختار Hibernate استراتيجية بناءً على dialect قاعدة البيانات. على PostgreSQL يستخدم تسلسلًا (sequence)؛ على MySQL يرجع إلى مولّد جدول. تجنّب AUTO في الإنتاج — قد يتغيّر سلوكه عند تبديل قواعد البيانات أو ترقية Hibernate.
- IDENTITY: يُفوِّض إلى عمود التزايد التلقائي في قاعدة البيانات (
SERIAL/AUTO_INCREMENT). بسيط، لكنه يُعطّل إدراج الدُّفعات عبر JDBC لأن Hibernate يجب أن يُفرغ كل صف منفردًا لاسترداد معرّفه المُولَّد قبل أن يتتالى إلى الكيانات التابعة. - SEQUENCE (موصى به لـ PostgreSQL / Oracle): يُخصّص كتلة من المعرّفات من تسلسل قاعدة البيانات في رحلة واحدة (
allocationSizeيتحكم في حجم الكتلة، افتراضيًا 50). يُعيّن Hibernate المعرّفات في الذاكرة ويُجمِّع عملياتINSERTبكفاءة. - TABLE: بديل محمول لكنه بطيء يستخدم جدول قفل مخصصًا. تجنّبه ما لم تكن قاعدة بياناتك تفتقر فعلًا إلى التسلسلات.
spring.jpa.properties.hibernate.jdbc.batch_size=50 في application.properties لن يُجدي نفعًا ما دام IDENTITY مُستخدَمًا.
UUID كمفاتيح أساسية
في الأنظمة الموزّعة أو الواجهات البرمجية التي تريد فيها كشف معرّفات الكيانات دون الكشف عن التسلسل، يُعدّ مفتاح UUID الأساسي نمطًا شائعًا. يدعم Hibernate 6 بشكل أصيل java.util.UUID:
يُفضَّل @UuidGenerator (خاص بـ Hibernate) على @GeneratedValue(strategy = GenerationType.UUID) (معيار JPA 3.1) لأنه يتيح التحكم في إصدار UUID. كلاهما يتجنّب رحلة ذهاب وإياب إلى قاعدة البيانات، لذا تُجمَّع عمليات الإدراج بشكل صحيح.
تعيينات الأنواع الأساسية للحقول
يُعيّن Hibernate أنواع Java القياسية إلى أنواع SQL تلقائيًا. التعيينات المهمة التي يجب معرفتها:
String→VARCHAR(255)افتراضيًا؛ تجاوز باستخدام@Column(length = N)أو@Lobللنصوص الكبيرة.int/Integerوlong/Long→INTEGER/BIGINT.boolean/Boolean→BOOLEAN(أوTINYINT(1)على MySQL).java.time.LocalDateوLocalDateTimeوInstant→ أعمدة تاريخ/وقت أصيلة. لا حاجة لـ@Temporalفي Hibernate 6.BigDecimal→DECIMAL؛ استخدمه دائمًا للقيم المالية — ولا تستخدم أبدًاdoubleأوfloatفي أعمدة مالية.- المُعدَّدات (Enums): علّق عليها بـ
@Enumerated(EnumType.STRING)لتخزين الاسم ("ACTIVE") بدلًا من الترتيب (0)، الذي ينكسر إذا تغيّر ترتيب المُعدَّد.
equals() و hashCode() للكيانات
يضع Hibernate الكيانات في مجموعات Set ويقارنها أثناء فحص التغييرات (dirty checking). يمكن أن يُنتج الافتراضي Object.equals() (مقارنة الهوية) أخطاء خفية عندما يُحمَّل نفس صف قاعدة البيانات في جلستَي EntityManager منفصلتين. الأسلوب الموصى به هو بناء المساواة على المفتاح التجاري الطبيعي (مثل sku) بدلًا من المعرّف البديل، لأن المعرّف البديل يكون null قبل أول استدعاء لـ save().
الخلاصة
الكيان هو فئة Java عادية مُعلَّمة بـ @Entity وحقل @Id واحد. استخدم @Table و@Column لتوثيق المخطط وتقييده. اختر استراتيجية التوليد بعناية: SEQUENCE للإنتاجية، IDENTITY للبساطة، UUID للأنظمة الموزّعة. استخدم BigDecimal للمبالغ المالية، و@Enumerated(EnumType.STRING) للمُعدَّدات، وأنواع java.time الحديثة للتواريخ. عرّف equals() على مفتاح تجاري لا على المعرّف البديل. مع هذه الأسس جاهزةً ستكون مستعدًا لاستكشاف تجريد المستودع (repository) في الدرس التالي.