الفصل 1: قواعد التسمية المتقدمة
مقدمة
في الوحدة 2، تعلمت 3 كلمات مفتاحية بسيطة: findBy، And، Or.
الآن، ستتعلم أكثر من 15 كلمة مفتاحية إضافية لإنشاء استعلامات معقدة!
ما ستتعلمه
- أكثر من 15 كلمة مفتاحية في Spring Data JPA
- كيفية دمجها
- أمثلة لكل كلمة مفتاحية
المدة: 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 | الاستخدام |
|---|---|---|---|
| And | findByNameAndPrice | WHERE name = ? AND price = ? | دمج الشروط |
| Or | findByNameOrCategory | WHERE name = ? OR category = ? | بديل |
| Between | findByPriceBetween | WHERE price BETWEEN ? AND ? | نطاق |
| LessThan | findByPriceLessThan | WHERE price < ? | أقل من |
| LessThanEqual | findByPriceLessThanEqual | WHERE price <= ? | أقل من أو يساوي |
| GreaterThan | findByPriceGreaterThan | WHERE price > ? | أكبر من |
| GreaterThanEqual | findByPriceGreaterThanEqual | WHERE price >= ? | أكبر من أو يساوي |
| Like | findByNameLike | WHERE name LIKE ? | نمط مع % |
| Containing | findByNameContaining | WHERE name LIKE %?% | يحتوي على |
| StartingWith | findByNameStartingWith | WHERE name LIKE ?% | يبدأ بـ |
| EndingWith | findByNameEndingWith | WHERE name LIKE %? | ينتهي بـ |
| In | findByCategoryIn | WHERE category IN (?) | في القائمة |
| NotIn | findByCategoryNotIn | WHERE category NOT IN (?) | ليس في القائمة |
| IsNull | findByDiscountIsNull | WHERE discount IS NULL | فارغ |
| IsNotNull | findByDiscountIsNotNull | WHERE discount IS NOT NULL | ليس فارغًا |
| True | findByActiveTrue | WHERE active = true | صحيح |
| False | findByActiveFalse | WHERE active = false | خاطئ |
| IgnoreCase | findByNameIgnoreCase | WHERE UPPER(name) = UPPER(?) | غير حساس للحالة |
| OrderBy | findByOrderByPriceAsc | ORDER 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
);
التقييم الذاتي
قم بإنشاء الطريقة ذهنيًا لـ:
- العثور على المنتجات بين 100€ و 500€
- العثور على المنتجات التي تحتوي على "phone" في الاسم
- العثور على المنتجات النشطة مع مخزون > 10
- العثور على المستخدمين الذين تم إنشاؤهم بعد تاريخ معين
- العثور على الطلبات غير المدفوعة
الإجابات:
findByPriceBetween(Double min, Double max)findByNameContaining(String keyword)findByActiveTrueAndStockGreaterThan(Integer minStock)findByCreatedAtAfter(LocalDate date)findByPaidFalse()
الخطوة التالية
أنت الآن تتقن قواعد التسمية! انتقل إلى العلاقات بين الكيانات.