انتقل إلى المحتوى الرئيسي

الفصل 1: قواعد التسمية المتقدمة

مقدمة​

في الوحدة 2، تعلمت 3 كلمات مفتاحية بسيطة: findBy، And، Or.

الآن، ستتعلم أكثر من 15 كلمة مفتاحية إضافية لإنشاء استعلامات معقدة!

ما ستتعلمه​

  1. أكثر من 15 كلمة مفتاحية في Spring Data JPA
  2. كيفية دمجها
  3. أمثلة لكل كلمة مفتاحية

المدة: 1 ساعة


كيف يعمل؟​

المبدأ السحري لـ Spring Data JPA​

public interface ProductRepository extends JpaRepository<Product, Long> {

List<Product> findByPriceLessThan(Double maxPrice);
// ^^^^^^ ^^^^^ ^^^^^^^
// find By PriceLessThan
}

Spring يقرأ اسم الطريقة ويولد SQL تلقائيًا!

-- SQL المولد تلقائيًا:
SELECT * FROM product WHERE price < ?

أنت لا تكتب SQL، Spring يقوم بذلك نيابة عنك!


هيكل أسماء الطرق​

findBy + [الحقل] + [العملية] + [حقل آخر] + [العملية]

أمثلة:

findByName                    // WHERE name = ?
findByPriceLessThan // WHERE price < ?
findByNameAndPrice // WHERE name = ? AND price = ?
findByPriceBetween // WHERE price BETWEEN ? AND ?

أكثر من 15 كلمة مفتاحية​

1. And / Or (أنت تعرفها بالفعل)​

دمج عدة شروط:

// AND
List<Product> findByNameAndPrice(String name, Double price);
// SQL: WHERE name = ? AND price = ?

// OR
List<Product> findByNameOrCategory(String name, String category);
// SQL: WHERE name = ? OR category = ?

مثال ملموس:

// العثور على منتج "Laptop" بسعر 999.99€
productRepo.findByNameAndPrice("Laptop", 999.99);

2. Between (بين قيمتين)​

العثور على القيم في نطاق معين:

List<Product> findByPriceBetween(Double min, Double max);
// SQL: WHERE price BETWEEN ? AND ?

مثال:

// منتجات بين 100€ و 500€
List<Product> products = productRepo.findByPriceBetween(100.0, 500.0);

حالات الاستخدام:

  • السعر في نطاق
  • التواريخ بين لحظتين
  • الأعمار بين الحد الأدنى والأقصى

3. LessThan / GreaterThan (مقارنات)​

مقارنة القيم الرقمية أو التواريخ:

// أقل من
List<Product> findByPriceLessThan(Double maxPrice);
// SQL: WHERE price < ?

// أقل من أو يساوي
List<Product> findByPriceLessThanEqual(Double maxPrice);
// SQL: WHERE price <= ?

// أكبر من
List<Product> findByPriceGreaterThan(Double minPrice);
// SQL: WHERE price > ?

// أكبر من أو يساوي
List<Product> findByPriceGreaterThanEqual(Double minPrice);
// SQL: WHERE price >= ?

أمثلة:

// منتجات بأقل من 100€
productRepo.findByPriceLessThan(100.0);

// منتجات بسعر 100€ أو أقل
productRepo.findByPriceLessThanEqual(100.0);

// منتجات بأكثر من 1000€
productRepo.findByPriceGreaterThan(1000.0);

حالات الاستخدام:

  • الحد الأدنى/الأقصى للسعر
  • الحد الأدنى للمخزون
  • الحد الأدنى/الأقصى للعمر
  • التواريخ قبل/بعد

4. Like / Containing / StartingWith / EndingWith (بحث نصي)​

البحث في سلاسل النصوص:

// LIKE (أنت توفر %)
List<Product> findByNameLike(String pattern);
// SQL: WHERE name LIKE ?
// الاستخدام: findByNameLike("%Laptop%")

// CONTAINING (Spring يضيف % تلقائيًا)
List<Product> findByNameContaining(String keyword);
// SQL: WHERE name LIKE ?
// الاستخدام: findByNameContaining("Laptop")
// يبحث عن: %Laptop%

// STARTING WITH (يبحث في البداية)
List<Product> findByNameStartingWith(String prefix);
// SQL: WHERE name LIKE ?
// الاستخدام: findByNameStartingWith("Mac")
// يبحث عن: Mac%

// ENDING WITH (يبحث في النهاية)
List<Product> findByNameEndingWith(String suffix);
// SQL: WHERE name LIKE ?
// الاستخدام: findByNameEndingWith("Pro")
// يبحث عن: %Pro

أمثلة:

// منتجات تحتوي على "phone" (iPhone، smartphone، إلخ.)
productRepo.findByNameContaining("phone");

// منتجات تبدأ بـ "Mac" (MacBook، Mac Mini، إلخ.)
productRepo.findByNameStartingWith("Mac");

// منتجات تنتهي بـ "Pro" (MacBook Pro، iPad Pro، إلخ.)
productRepo.findByNameEndingWith("Pro");

الفرق بين Like و Containing:

// Like - أنت تتحكم في %
findByNameLike("%Laptop%") // يحتوي على Laptop
findByNameLike("Laptop%") // يبدأ بـ Laptop
findByNameLike("%Laptop") // ينتهي بـ Laptop

// Containing - Spring يضيف %
findByNameContaining("Laptop") // يحتوي على Laptop (%Laptop%)

5. In / NotIn (في قائمة)​

التحقق مما إذا كانت القيمة في قائمة:

// IN
List<Product> findByCategoryIn(List<String> categories);
// SQL: WHERE category IN (?, ?, ?)

// NOT IN
List<Product> findByCategoryNotIn(List<String> categories);
// SQL: WHERE category NOT IN (?, ?, ?)

أمثلة:

// منتجات في عدة فئات
List<String> categories = Arrays.asList("Electronics", "Computers", "Phones");
productRepo.findByCategoryIn(categories);

// منتجات ليست في هذه الفئات
productRepo.findByCategoryNotIn(categories);

حالات الاستخدام:

  • التصفية حسب عدة فئات
  • استبعاد بعض الحالات
  • البحث بواسطة عدة معرفات

6. IsNull / IsNotNull (قيم فارغة)​

التحقق مما إذا كان الحقل فارغًا:

// IS NULL
List<Product> findByDiscountIsNull();
// SQL: WHERE discount IS NULL

// IS NOT NULL (أو NotNull)
List<Product> findByDiscountIsNotNull();
List<Product> findByDiscountNotNull();
// SQL: WHERE discount IS NOT NULL

أمثلة:

// منتجات بدون خصم
productRepo.findByDiscountIsNull();

// منتجات مع خصم
productRepo.findByDiscountIsNotNull();

حالات الاستخدام:

  • منتجات بدون صورة
  • طلبات بدون تاريخ تسليم
  • مستخدمون بدون بريد إلكتروني

7. True / False (قيم منطقية)​

التحقق من القيم المنطقية:

// TRUE
List<Product> findByActiveTrue();
// SQL: WHERE active = true

// FALSE
List<Product> findByActiveFalse();
// SQL: WHERE active = false

أمثلة:

// منتجات نشطة
productRepo.findByActiveTrue();

// منتجات غير نشطة
productRepo.findByActiveFalse();

حالات الاستخدام:

  • منتجات نشطة/غير نشطة
  • مستخدمون موثقون/غير موثقون
  • طلبات مدفوعة/غير مدفوعة

8. IgnoreCase (تجاهل الحالة)​

بحث غير حساس للحالة:

// حقل واحد
List<Product> findByNameIgnoreCase(String name);
// SQL: WHERE UPPER(name) = UPPER(?)

// عدة حقول
List<Product> findByNameAndCategoryAllIgnoreCase(String name, String category);
// SQL: WHERE UPPER(name) = UPPER(?) AND UPPER(category) = UPPER(?)

أمثلة:

// يجد "laptop"، "LAPTOP"، "Laptop"، إلخ.
productRepo.findByNameIgnoreCase("laptop");

حالات الاستخدام:

  • بحث المستخدم (غير حساس للحالة)
  • بحث المنتج
  • البريد الإلكتروني (john@email.com = JOHN@email.com)

9. OrderBy (ترتيب)​

ترتيب النتائج:

// ترتيب تصاعدي
List<Product> findByCategoryOrderByPriceAsc(String category);
// SQL: WHERE category = ? ORDER BY price ASC

// ترتيب تنازلي
List<Product> findByCategoryOrderByPriceDesc(String category);
// SQL: WHERE category = ? ORDER BY price DESC

// ترتيب متعدد
List<Product> findByCategoryOrderByPriceAscNameDesc(String category);
// SQL: WHERE category = ? ORDER BY price ASC, name DESC

أمثلة:

// لابتوبات من الأرخص إلى الأغلى
productRepo.findByCategoryOrderByPriceAsc("Laptops");

// لابتوبات من الأغلى إلى الأرخص
productRepo.findByCategoryOrderByPriceDesc("Laptops");

تركيبات قوية​

مثال 1: بحث معقد في التجارة الإلكترونية​

public interface ProductRepository extends JpaRepository<Product, Long> {

// منتجات نشطة، في نطاق سعر، مرتبة حسب الاسم
List<Product> findByActiveTrueAndPriceBetweenOrderByNameAsc(
Double minPrice,
Double maxPrice
);
// SQL: WHERE active = true
// AND price BETWEEN ? AND ?
// ORDER BY name ASC
}

الاستخدام:

// منتجات نشطة بين 100€ و 500€، مرتبة حسب الاسم
productRepo.findByActiveTrueAndPriceBetweenOrderByNameAsc(100.0, 500.0);

مثال 2: إدارة المخزون​

public interface ProductRepository extends JpaRepository<Product, Long> {

// منتجات نفدت من المخزون في فئة معينة
List<Product> findByCategoryAndStockLessThan(String category, Integer minStock);
// SQL: WHERE category = ? AND stock < ?

// منتجات مع مخزون متاح، باستثناء بعض الفئات
List<Product> findByStockGreaterThanAndCategoryNotIn(
Integer minStock,
List<String> excludedCategories
);
// SQL: WHERE stock > ? AND category NOT IN (?, ?, ?)
}

مثال 3: بحث المستخدمين​

public interface UserRepository extends JpaRepository<User, Long> {

// مستخدمون نشطون مع بريد إلكتروني موثق
List<User> findByActiveTrueAndEmailVerifiedTrue();

// مستخدمون تم إنشاؤهم في فترة معينة
List<User> findByCreatedAtBetween(LocalDate start, LocalDate end);

// مستخدمون مع اسم يحتوي على كلمة مفتاحية (غير حساس للحالة)
List<User> findByNameContainingIgnoreCase(String keyword);
}

جدول ملخص لأكثر من 15 كلمة مفتاحية​

الكلمة المفتاحيةالصيغةSQLالاستخدام
AndfindByNameAndPriceWHERE name = ? AND price = ?دمج الشروط
OrfindByNameOrCategoryWHERE name = ? OR category = ?بديل
BetweenfindByPriceBetweenWHERE price BETWEEN ? AND ?نطاق
LessThanfindByPriceLessThanWHERE price < ?أقل من
LessThanEqualfindByPriceLessThanEqualWHERE price <= ?أقل من أو يساوي
GreaterThanfindByPriceGreaterThanWHERE price > ?أكبر من
GreaterThanEqualfindByPriceGreaterThanEqualWHERE price >= ?أكبر من أو يساوي
LikefindByNameLikeWHERE name LIKE ?نمط مع %
ContainingfindByNameContainingWHERE name LIKE %?%يحتوي على
StartingWithfindByNameStartingWithWHERE name LIKE ?%يبدأ بـ
EndingWithfindByNameEndingWithWHERE name LIKE %?ينتهي بـ
InfindByCategoryInWHERE category IN (?)في القائمة
NotInfindByCategoryNotInWHERE category NOT IN (?)ليس في القائمة
IsNullfindByDiscountIsNullWHERE discount IS NULLفارغ
IsNotNullfindByDiscountIsNotNullWHERE discount IS NOT NULLليس فارغًا
TruefindByActiveTrueWHERE active = trueصحيح
FalsefindByActiveFalseWHERE active = falseخاطئ
IgnoreCasefindByNameIgnoreCaseWHERE UPPER(name) = UPPER(?)غير حساس للحالة
OrderByfindByOrderByPriceAscORDER BY price ASCترتيب

نصائح وممارسات جيدة​

1. أسماء طرق قابلة للقراءة​

✅ جيد:

findByPriceBetweenAndCategoryOrderByNameAsc
  • قابل للقراءة
  • واضح
  • يتبع القواعد

❌ سيء:

findByPAndC  // قصير جدًا
findProductsWherePriceIsBetweenMinAndMaxAndCategoryEqualsGivenCategoryOrderedByName // طويل جدًا

2. تحديد التعقيد​

إذا أصبحت الطريقة معقدة جدًا، استخدم @Query:

// ❌ معقد جدًا
findByActiveTrueAndPriceBetweenAndCategoryInAndStockGreaterThanAndNameContainingOrderByPriceAscNameDesc

// ✅ استخدم @Query بدلاً من ذلك
@Query("SELECT p FROM Product p WHERE p.active = true AND ...")
List<Product> findComplexQuery(...);

3. اختبار الطرق​

@Test
public void testFindByPriceBetween() {
// ترتيب
productRepo.save(new Product("Cheap", 50.0));
productRepo.save(new Product("Medium", 150.0));
productRepo.save(new Product("Expensive", 500.0));

// تنفيذ
List<Product> results = productRepo.findByPriceBetween(100.0, 200.0);

// تحقق
assertEquals(1, results.size());
assertEquals("Medium", results.get(0).getName());
}

ملخص الفصل​

ما تعلمته​

✅ أكثر من 15 كلمة مفتاحية لإنشاء استعلامات
✅ دمج الكلمات المفتاحية
✅ بحث نصي (Like، Containing، إلخ.)
✅ مقارنات (LessThan، GreaterThan، Between)
✅ قوائم (In، NotIn)
✅ Null (IsNull، IsNotNull)
✅ قيم منطقية (True، False)
✅ ترتيب (OrderBy)

الآن يمكنك​

// إنشاء استعلامات معقدة بدون SQL!
List<Product> findByActiveTrueAndPriceBetweenAndCategoryInOrderByPriceAsc(
Double min,
Double max,
List<String> categories
);

التقييم الذاتي​

قم بإنشاء الطريقة ذهنيًا لـ:

  1. العثور على المنتجات بين 100€ و 500€
  2. العثور على المنتجات التي تحتوي على "phone" في الاسم
  3. العثور على المنتجات النشطة مع مخزون > 10
  4. العثور على المستخدمين الذين تم إنشاؤهم بعد تاريخ معين
  5. العثور على الطلبات غير المدفوعة

الإجابات:

  1. findByPriceBetween(Double min, Double max)
  2. findByNameContaining(String keyword)
  3. findByActiveTrueAndStockGreaterThan(Integer minStock)
  4. findByCreatedAtAfter(LocalDate date)
  5. findByPaidFalse()

الخطوة التالية​

أنت الآن تتقن قواعد التسمية! انتقل إلى العلاقات بين الكيانات.

➡️ الفصل 2: العلاقات بين الكيانات

⬅️ العودة إلى الوحدة 6