الفصل 1: إعداد مصادر البيانات
مقدمة
في كل مرة يتفاعل تطبيق Spring Boot الخاص بك مع قاعدة البيانات، فإنه يستخدم اتصالاً. ولكن من أين تأتي هذه الاتصالات؟ كيف تتم إدارتها؟ هذا هو دور مصدر البيانات.
🎯 أهداف الفصل
في نهاية هذا الفصل، ستعرف:
- ✅ ما هو مصدر البيانات ولماذا هو مهم
- ✅ كيف يقوم Spring Boot بتكوين الاتصالات تلقائيًا
- ✅ تكوين HikariCP (المجموعة الافتراضية)
- ✅ تحسين الإعدادات للإنتاج
- ✅ تكوين مصادر بيانات متعددة
1. ما هو مصدر البيانات؟
تعريف بسيط
مصدر البيانات هو كائن يوفر اتصالات لقاعدة البيانات.
المشكلة بدون مجموعة اتصالات
// ❌ ممارسة سيئة: اتصال جديد لكل طلب
public void getUser(Long id) {
Connection conn = DriverManager.getConnection(url, user, password);
// ... استخدام الاتصال
conn.close();
}
مشاكل:
- إنشاء اتصال = 500ms إلى 1s (بطيء جدًا!)
- 100 مستخدم متزامن = 100 اتصال = قاعدة بيانات مشبعة
- عدم إعادة الاستخدام = إهدار الموارد
الحل: مجموعة اتصالات
// ✅ ممارسة جيدة: إعادة استخدام الاتصالات
public void getUser(Long id) {
Connection conn = dataSource.getConnection(); // ← اتصال من المجموعة (< 1ms)
// ... استخدام الاتصال
conn.close(); // ← يعود إلى المجموعة (ليس مغلقًا حقًا)
}
فوائد:
- اتصالات مسبقة الإنشاء = استجابة فورية
- إعادة الاستخدام = حمل أقل على قاعدة البيانات
- يحد من العدد الأقصى للاتصالات
2. HikariCP: المجموعة الافتراضية
لماذا HikariCP؟
يستخدم Spring Boot HikariCP (光 = "الضوء" باليابانية) لأنه:
- 🚀 الأسرع (معايير مثبتة)
- 💪 الأكثر موثوقية (يستخدم من قبل شركات كبيرة)
- 🔧 سهل التكوين
التكوين التلقائي لـ Spring Boot
عندما تضيف اعتماد قاعدة بيانات، يقوم Spring Boot بتكوين HikariCP تلقائيًا:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<!-- HikariCP مضمن تلقائيًا! -->
</dependency>
3. التكوين الأساسي
التكوين الأدنى (H2)
# application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
تكوين PostgreSQL
# application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=secret
تكوين MySQL
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=secret
4. تكوين HikariCP المتقدم
الإعدادات الأساسية
# application.properties
# حجم المجموعة
spring.datasource.hikari.minimum-idle=5 # الحد الأدنى للاتصالات المحفوظة
spring.datasource.hikari.maximum-pool-size=10 # الحد الأقصى للاتصالات
# المهلات الزمنية
spring.datasource.hikari.connection-timeout=30000 # الانتظار الأقصى للحصول على اتصال (30 ثانية)
spring.datasource.hikari.idle-timeout=600000 # الوقت قبل إغلاق اتصال غير مستخدم (10 دقائق)
spring.datasource.hikari.max-lifetime=1800000 # العمر الأقصى للاتصال (30 دقيقة)
# اسم المجموعة (للسجلات)
spring.datasource.hikari.pool-name=MyAppPool
تصور المجموعة
┌─────────────────────────────────────────────────────┐
│ HikariCP Pool │
│ │
│ minimum-idle=5 maximum-pool-size=10 │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Conn1│ │Conn2│ │Conn3│ │Conn4│ │Conn5│ (idle) │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ │
│ + 5 اتصالات إضافية إذا لزم الأمر │
│ │
│ connection-timeout=30s (الانتظار الأقصى إذا كانت المجموعة ممتلئة) │
└─────────────────────────────────────────────────────┘
صيغة YAML (بديل)
# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: postgres
password: secret
hikari:
minimum-idle: 5
maximum-pool-size: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyAppPool
5. توصيات حسب البيئة
التطوير (H2)
# application-dev.properties
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.maximum-pool-size=5
# تفعيل وحدة تحكم H2
spring.h2.console.enabled=true
الإنتاج (PostgreSQL)
# application-prod.properties
spring.datasource.url=jdbc:postgresql://db.server.com:5432/proddb
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=50
# لا وحدة تحكم H2!
spring.h2.console.enabled=false
# التحقق من الاتصالات
spring.datasource.hikari.connection-test-query=SELECT 1
صيغة لحجم المجموعة الأقصى
قاعدة عامة:
maximum-pool-size = (عدد وحدات المعالجة المركزية * 2) + عدد الأقراص
مثال:
- خادم مع 4 وحدات معالجة مركزية وقرص SSD واحد
- حجم المجموعة = (4 * 2) + 1 = 9 ≈ 10
لا تبالغ في الحجم! مجموعة كبيرة جدًا يمكن أن تؤدي إلى تدهور الأداء لأن قاعدة البيانات يجب أن تدير الكثير من الاتصالات المتزامنة.
6. تكوين مصادر بيانات متعددة
حالة الاستخدام
أحيانًا، يجب على التطبيق الاتصال بـ عدة قواعد بيانات:
التكوين
# application.properties
# مصدر البيانات الأساسي (PostgreSQL)
spring.datasource.primary.url=jdbc:postgresql://localhost:5432/usersdb
spring.datasource.primary.username=postgres
spring.datasource.primary.password=secret
spring.datasource.primary.driver-class-name=org.postgresql.Driver
# مصدر البيانات الثانوي (MySQL)
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/productsdb
spring.datasource.secondary.username=root
spring.datasource.secondary.password=secret
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
تكوين Java
@Configuration
public class DataSourceConfig {
// مصدر البيانات الأساسي
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
// مصدر البيانات الثانوي
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
الاستخدام مع JPA
// تكوين EntityManager للبيانات الأساسية
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository.users",
entityManagerFactoryRef = "primaryEntityManager",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDatabaseConfig {
// ... التكوين
}
// تكوين EntityManager للبيانات الثانوية
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository.products",
entityManagerFactoryRef = "secondaryEntityManager",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDatabaseConfig {
// ... التكوين
}
7. مراقبة المجموعة
عبر Actuator
أضف Spring Boot Actuator لمراقبة المجموعة:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.properties
management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.show-details=always
المقاييس المتاحة
# تحقق من المجموعة
GET /actuator/metrics/hikaricp.connections
# الاتصالات النشطة
GET /actuator/metrics/hikaricp.connections.active
# الاتصالات المعلقة
GET /actuator/metrics/hikaricp.connections.pending
# المهلات الزمنية
GET /actuator/metrics/hikaricp.connections.timeout
مثال على الاستجابة
{
"name": "hikaricp.connections.active",
"measurements": [
{
"statistic": "VALUE",
"value": 3.0
}
],
"availableTags": [
{
"tag": "pool",
"values": ["MyAppPool"]
}
]
}
8. المشاكل الشائعة والحلول
❌ مهلة الاتصال
HikariPool-1 - Connection is not available, request timed out after 30000ms.
الأسباب:
- مجموعة صغيرة جدًا
- اتصالات غير مغلقة (تسرب)
- طلبات بطيئة جدًا
الحلول:
# زيادة حجم المجموعة
spring.datasource.hikari.maximum-pool-size=20
# زيادة المهلة
spring.datasource.hikari.connection-timeout=60000
# تفعيل سجلات التسرب
spring.datasource.hikari.leak-detection-threshold=60000
❌ عدد كبير جدًا من الاتصالات
PostgreSQL: FATAL: too many connections for role "postgres"
السبب: مجموعة كبيرة جدًا أو عدة مثيلات
الحل:
# تقليل حجم المجموعة
spring.datasource.hikari.maximum-pool-size=5
# تحقق من max_connections في PostgreSQL
# SHOW max_connections;
❌ تسرب الاتصال
HikariPool-1 - Connection leak detection triggered
السبب: اتصال لم يتم إرجاعه إلى المجموعة
الحل:
# تفعيل اكتشاف التسرب
spring.datasource.hikari.leak-detection-threshold=30000
ثم العثور على الكود الخاطئ وتصحيحه (اتصال غير مغلق).
9. ملخص
النقاط الرئيسية
| المفهوم | الوصف |
|---|---|
| مصدر البيانات | يوفر الاتصالات لقاعدة البيانات |
| HikariCP | مجموعة الاتصالات الافتراضية (الأسرع) |
| minimum-idle | الحد الأدنى للاتصالات المحفوظة |
| maximum-pool-size | الحد الأقصى للاتصالات |
| connection-timeout | الوقت الأقصى للحصول على اتصال |
قائمة التحقق من التكوين
✅ تحديد spring.datasource.url
✅ تحديد اسم المستخدم/كلمة المرور
✅ تكوين minimum-idle و maximum-pool-size
✅ ضبط المهلات الزمنية حسب البيئة
✅ المراقبة باستخدام Actuator
التكوين الموصى به
# الإنتاج
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=myuser
spring.datasource.password=${DB_PASSWORD}
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.pool-name=MyAppPool
🎯 الخطوة التالية
الآن بعد أن فهمت كيف تتم إدارة الاتصالات، حان الوقت لتعلم كيفية حماية سلامة بياناتك باستخدام المعاملات.
➡️ الفصل 2: إدارة المعاملات (@Transactional)