0Pricing
Java Academy · درس

علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد

اربط @OneToMany و@ManyToMany و@JoinTable مع أنواع التتابع واستراتيجيات الجلب

علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

أنواع العلاقات في JPA

يدعم JPA أربعة أنواع من العلاقات: @OneToOne، و@OneToMany، و@ManyToOne، و@ManyToMany. ويعيّن كل نوع قيود المفاتيح الخارجية أو جداول الربط.

@ManyToOne: الجانب المالك

تُرسم علاقة متعدد إلى واحد (مثل انتماء عدة Orders إلى User واحد) باستخدام @ManyToOne و@JoinColumn للمفتاح الخارجي.

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

@OneToMany: الجانب العكسي

يستخدم الجانب العكسي (يمتلك User عدة Orders) الصيغة @OneToMany(mappedBy = "user"). وتشير سمة mappedBy إلى الحقل في الجانب المالك الذي يحتوي على المفتاح الخارجي.

@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();
}

مساعد الحفاظ على الاتساق ثنائي الاتجاه

عند استخدام الارتباطات ثنائية الاتجاه، حافظوا على جانبي العلاقة في أسلوب مساعد للحفاظ على اتساق مخطط الكائنات قبل تنفيذ flush.

public void addOrder(Order order) {
    orders.add(order);
    order.setUser(this); // keep both sides in sync
}
public void removeOrder(Order order) {
    orders.remove(order);
    order.setUser(null);
}

أنواع Cascade

تنشر CascadeType.ALL جميع العمليات (persist وmerge وremove وrefresh وdetach) من الكيان الأب إلى الكيان الابن. وتُعد PERSIST وMERGE خيارين شائعين وآمنين. يؤدي REMOVE إلى حذف الكيانات الابنة عند حذف الكيان الأب.

@OneToMany(mappedBy = "user",
           cascade = {CascadeType.PERSIST, CascadeType.MERGE},
           orphanRemoval = true)
private List<Order> orders;

orphanRemoval: الحذف التلقائي للكيان الابن

تحذف orphanRemoval = true كيانًا ابنًا عند إزالته من مجموعة الكيان الأب — حتى من دون استدعاء صريح لـ entityManager.remove().

user.getOrders().remove(order); // triggers DELETE for the orphaned order on flush

@ManyToMany مع @JoinTable

تستخدم العلاقة متعدد إلى متعدد (مثل Student وCourse) جدول ربط. عرّفوه في الجانب المالك باستخدام @JoinTable؛ أما الجانب العكسي فيستخدم mappedBy.

@Entity
public class Student {
    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id"))
    private Set<Course> courses = new HashSet<>();
}

أعمدة إضافية في جدول الربط

عندما يحتاج جدول الربط إلى أعمدة إضافية (مثل تاريخ التسجيل)، حوّلوا @ManyToMany إلى علاقتين من نوع @OneToMany / @ManyToOne مع كيان جديد يمثّل جدول الربط.

@Entity
public class Enrollment {
    @ManyToOne Student student;
    @ManyToOne Course course;
    private LocalDate enrolledAt;
}

FetchType: LAZY مقابل EAGER

الجلب الافتراضي: تكون @ManyToOne و@OneToOne من النوع EAGER (تُحمّلان فورًا)، بينما تكون @OneToMany و@ManyToMany من النوع LAZY (تُحمّلان عند الوصول إليهما). استخدموا LAZY دائمًا للمجموعات لتجنب استعلامات N+1.

// Always override to LAZY:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;

مشكلة استعلامات N+1

يؤدي الوصول إلى مجموعة كسولة داخل حلقة إلى تنفيذ استعلام واحد لكل كيان (N+1). عالجوا ذلك باستخدام JOIN FETCH في JPQL أو @EntityGraph لتحميل المجموعة باستعلام واحد.

// N+1 problem:
List<User> users = repo.findAll();
users.forEach(u -> u.getOrders().size()); // 1 + N queries!
// Fix:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();

Set مقابل List للمجموعات

استخدموا Set مع @ManyToMany لتجنب صفوف الربط المكررة. واستخدموا List مع @OneToMany عندما يكون الترتيب مهمًا. لا تستخدموا List مع @ManyToMany مطلقًا — إذ يتسبب ذلك في حذف جميع الصفوف وإعادة إدراجها بواسطة Hibernate.

تحقق سريع

ماذا تشير mappedBy إليه في التعليق التوضيحي @OneToMany؟

مراجعة

استخدموا @ManyToOne+@JoinColumn في الجانب المالك (جانب المفتاح الخارجي). واستخدموا @OneToMany(mappedBy=...) في الجانب العكسي. استخدموا LAZY دائمًا للمجموعات. عالجوا مشكلة N+1 باستخدام JOIN FETCH. واستخدموا كيان ربط للعلاقة متعدد إلى متعدد التي تحتوي على أعمدة إضافية.

الأسئلة الشائعة

هل درس «علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد» مجاني؟

نعم — نص درس «علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد»؟

اربط @OneToMany و@ManyToMany و@JoinTable مع أنواع التتابع واستراتيجيات الجلب تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ربط الكيانات باستخدام تعليقات JPA
  2. مستودعات Spring Data وطرائق الاستعلام
  3. علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد
  4. التقسيم إلى صفحات والفرز والإسقاطات
← العودة إلى Java Academy