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

TP4.1 - دمج PostgreSQL مع Jakarta Persistence

الانتقال إلى قاعدة بيانات الإنتاج​

الأهداف​

سيمكنك هذا العمل العملي من:

  • دمج PostgreSQL مع Spring Boot
  • استخدام Jakarta Persistence (JPA) لحفظ البيانات
  • تكوين Hibernate كـ ORM
  • إنشاء وإدارة قاعدة بيانات علائقية
  • فهم الفرق بين Jakarta و Javax Persistence
  • إتقان التكوين باستخدام application.properties و application.yml

جدول المحتويات​

  1. تذكير سريع
  2. إضافة قاعدة البيانات
  3. اختبار مع PostgreSQL
  4. تمرين 1: Javax Persistence
  5. تمرين 2: تكوين YAML
  6. تمرين 3: Lombok
  7. تمرين 4: إعادة الإنشاء التلقائي
  8. تمرين 5: قاعدة بيانات أخرى

1. تذكير سريع​

السياق​

في الأعمال العملية السابقة، قمنا بإنشاء تطبيق CRUD كامل مع H2. الآن، سننتقل إلى PostgreSQL، وهي قاعدة بيانات علائقية احترافية مناسبة للإنتاج.

أهداف هذا العمل العملي​

  1. الانتقال من H2 إلى PostgreSQL - الانتقال من قاعدة مدمجة إلى قاعدة إنتاج
  2. استخدام Jakarta Persistence (JPA) - ربط كائنات Java بالجداول SQL
  3. تكوين PostgreSQL - تثبيت وتكوين قاعدة بيانات احترافية

المتطلبات المسبقة​

  • إكمال TP3 (هيكلية CRUD)
  • تثبيت PostgreSQL (الإصدار 12 أو أعلى)
  • pgAdmin أو DBeaver لإدارة PostgreSQL
  • Visual Studio Code مع ملحقات Java

2. إضافة قاعدة البيانات​

2.1 إضافة التبعيات في pom.xml​

افتح ملف pom.xml وأضف التبعيات التالية في قسم <dependencies>:

<!-- Driver PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<!-- Spring Data JPA (inclut Jakarta Persistence) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

التفسير:

التبعيةالدور
postgresqlبرنامج تشغيل JDBC للاتصال بـ PostgreSQL
spring-boot-starter-data-jpaيتضمن JPA و Hibernate و Spring Data

ملاحظة: بعد الإضافة، سيقترح عليك VS Code إعادة تحميل مشروع Maven. اقبل أو نفذ:

mvn clean install -DskipTests

2.2 تكوين application.properties​

قم بإنشاء أو تعديل ملف src/main/resources/application.properties:

# URL de connexion à la base de données
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db?useSSL=false

# Credentials de connexion
spring.datasource.username=postgres
spring.datasource.password=postgres

# Configuration Hibernate
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Stratégie de génération du schéma
spring.jpa.hibernate.ddl-auto=create

# Afficher les requêtes SQL (utile pour le développement)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

تفسير الخصائص:

الخاصيةالوصفالقيم الممكنة
spring.datasource.urlURL اتصال JDBCالصيغة: jdbc:postgresql://host:port/database
spring.datasource.usernameاسم مستخدم PostgreSQLالافتراضي: postgres
spring.datasource.passwordكلمة مرور PostgreSQLيجب تعديلها حسب التثبيت الخاص بك
spring.jpa.hibernate.ddl-autoاستراتيجية إنشاء المخططnone, validate, update, create, create-drop
spring.jpa.show-sqlعرض استعلامات SQL في وحدة التحكمtrue أو false

قيم ddl-auto:

مهم: قم بتغيير greeting_db إلى اسم قاعدة البيانات الخاصة بك إذا لزم الأمر.

2.3 توضيح الكيان Greeting.java​

قم بتعديل فئة Greeting.java لجعلها كيان JPA:

package com.example.demo;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "greeting")
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

// Constructeur par défaut (requis par JPA)
public Greeting() {
}

// Constructeur avec paramètres
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}

// Getters et Setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

تفسير التعليقات التوضيحية:

التعليق التوضيحيالدور
@Entityيحدد الفئة ككيان JPA (مرتبط بجدول)
@Table(name = "greeting")يحدد اسم الجدول SQL
@Idيحدد الحقل كمفتاح أساسي
@GeneratedValue(strategy = GenerationType.IDENTITY)زيادة تلقائية للمعرف
@Column(name = "content", nullable = false)تكوين العمود SQL

هيكلية JPA:

2.4 إنشاء المستودع​

قم بإنشاء واجهة GreetingRepository.java في الحزمة com.example.demo:

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
// Spring Data JPA génère automatiquement l'implémentation
// Méthodes disponibles: save(), findAll(), findById(), delete(), etc.
}

الطرق المولدة تلقائيًا:

الطريقةالوصف
save(Greeting g)حفظ أو تحديث
findAll()استرجاع جميع الإدخالات
findById(Long id)استرجاع بواسطة المعرف
deleteById(Long id)حذف بواسطة المعرف
count()عد الإدخالات

2.5 تعديل وحدة التحكم​

قم بتعديل GreetingController.java لاستخدام المستودع:

package com.example.demo;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();

private final GreetingRepository greetingRepository;

public GreetingController(GreetingRepository greetingRepository) {
this.greetingRepository = greetingRepository;
}

@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value="name") String name) {
Greeting greeting = new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);

// Sauvegarder dans la base de données
return greetingRepository.save(greeting);
}
}

تدفق البيانات:

2.6 إنشاء قاعدة بيانات PostgreSQL​

الخيار أ: باستخدام pgAdmin​

  1. فتح pgAdmin

  2. إنشاء مستخدم (اختياري):

    • انقر بزر الماوس الأيمن على "Login/Group Roles"
    • "Create" → "Login/Group Role"
    • علامة التبويب "General": Name = postgres
    • علامة التبويب "Definition": Password = postgres
    • علامة التبويب "Privileges": تحديد "Can login?" و "Create databases?"
    • حفظ
  3. إنشاء قاعدة البيانات:

    • انقر بزر الماوس الأيمن على "Databases"
    • "Create" → "Database"
    • Database name: greeting_db
    • Owner: postgres
    • حفظ

الخيار ب: باستخدام SQL (سطر الأوامر)​

-- Créer la base de données
CREATE DATABASE greeting_db;

-- Créer l'utilisateur (si nécessaire)
CREATE USER postgres WITH PASSWORD 'postgres';

-- Accorder les privilèges
GRANT ALL PRIVILEGES ON DATABASE greeting_db TO postgres;

لتنفيذ هذه الأوامر:

# Windows
psql -U postgres

# macOS/Linux
sudo -u postgres psql

3. اختبار مع PostgreSQL​

3.1 التحقق من التكوين​

قبل تشغيل التطبيق، تحقق من:

قائمة التحقق:

  • PostgreSQL قيد التشغيل
  • قاعدة البيانات greeting_db موجودة
  • المستخدم postgres لديه الصلاحيات الصحيحة
  • ملف application.properties صحيح
  • الفئة Greeting تحتوي على التعليق التوضيحي @Entity
  • الواجهة GreetingRepository تم إنشاؤها
  • وحدة التحكم GreetingController تستخدم المستودع

3.2 تشغيل التطبيق​

في VS Code:

الطريقة 1: عبر الطرفية​

# Nettoyer et compiler
mvn clean install

# Lancer l'application
mvn spring-boot:run

الطريقة 2: عبر الواجهة​

  1. فتح GreetingApplication.java
  2. انقر على "Run" فوق طريقة main
  3. أو اضغط على F5

السجلات المتوقعة:

Hibernate: drop table if exists greeting cascade
Hibernate: create table greeting (id bigserial not null, content varchar(255) not null, primary key (id))
Started GreetingApplication in X.XXX seconds

3.3 اختبار النقاط النهائية​

اختبار 1: إنشاء تحية

http://localhost:8080/greeting?name=John

الاستجابة المتوقعة:

{
"id": 1,
"content": "Hello, John!"
}

اختبار 2: إنشاء تحيات متعددة

http://localhost:8080/greeting?name=Alice
http://localhost:8080/greeting?name=Bob
http://localhost:8080/greeting?name=Charlie

3.4 التحقق في PostgreSQL​

باستخدام pgAdmin:​

  1. فتح pgAdmin
  2. التنقل: Servers → PostgreSQL → Databases → greeting_db → Schemas → public → Tables
  3. انقر بزر الماوس الأيمن على "greeting" → "View/Edit Data" → "All Rows"

باستخدام SQL:​

-- Se connecter à la base
\c greeting_db

-- Afficher toutes les entrées
SELECT * FROM greeting;

-- Résultat attendu:
-- id | content
-- ---+----------------
-- 1 | Hello, John!
-- 2 | Hello, Alice!
-- 3 | Hello, Bob!
-- 4 | Hello, Charlie!

3.5 سجلات SQL في وحدة التحكم​

بفضل spring.jpa.show-sql=true، سترى استعلامات SQL في وحدة التحكم:

Hibernate: insert into greeting (content) values (?)

4. تمرين 1: إعادة التنفيذ مع Javax Persistence​

البيان​

أعد تنفيذ المشروع باستخدام Javax Persistence بدلاً من Jakarta Persistence (الإصدار القديم من JPA).

الهدف​

فهم الفرق بين Jakarta و Javax Persistence.

السياق التاريخي​

التعليمات​

الخطوة 1: تعديل pom.xml

أضف تبعية Javax Persistence:

<!-- Ajout de Javax Persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>

الخطوة 2: تعديل الواردات في Greeting.java

استبدل:

import jakarta.persistence.*;

بـ:

import javax.persistence.*;

الخطوة 3: الفئة الكاملة مع Javax

package com.example.demo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "greeting")
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

public Greeting() {}

public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}

// Getters et Setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

الخطوة 4: الاختبار

  1. إعادة الترجمة: mvn clean install -DskipTests
  2. إعادة تشغيل التطبيق
  3. التحقق من أن كل شيء يعمل

أسئلة للتفكير​

  1. ما هو الفرق الرئيسي بين Javax و Jakarta Persistence؟
  2. لماذا يوصى باستخدام Jakarta للمشاريع الجديدة؟
  3. هل يمكن خلط الاثنين في نفس المشروع؟

5. تمرين 2: التكوين مع application.yml​

البيان​

قم بتحويل تكوين application.properties إلى صيغة YAML.

الهدف​

إتقان صيغة YAML لتكوين Spring Boot.

التعليمات​

الخطوة 1: حذف application.properties

احذف أو أعد تسمية ملف application.properties.

الخطوة 2: إنشاء application.yml

قم بإنشاء src/main/resources/application.yml:

spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
username: postgres
password: postgres

jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true

الخطوة 3: الاختبار

أعد تشغيل التطبيق وتحقق من أن كل شيء يعمل.

المقارنة​

application.properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db
spring.datasource.username=postgres
spring.jpa.hibernate.ddl-auto=create

application.yml:

spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db
username: postgres
jpa:
hibernate:
ddl-auto: create

مزايا YAML:

  • أكثر قابلية للقراءة للهياكل الهرمية
  • أقل تكرار
  • صيغة حديثة

6. تمرين 3: التبسيط مع Lombok​

البيان​

قم بإزالة getters و setters والمنشئين اليدويين باستخدام Lombok.

الهدف​

تقليل الكود المتكرر باستخدام Lombok.

المتطلبات المسبقة​

الخطوة 1: إضافة تبعية Lombok في pom.xml

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

الخطوة 2: تثبيت ملحق Lombok في VS Code

  1. الملحقات → البحث عن "Lombok"
  2. تثبيت "Lombok Annotations Support for VS Code"

التعليمات​

تعديل Greeting.java:

package com.example.demo;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "greeting")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

// Lombok génère automatiquement:
// - Getters
// - Setters
// - toString()
// - equals() et hashCode()
// - Constructeur vide
// - Constructeur avec tous les arguments
}

التعليقات التوضيحية Lombok:

التعليق التوضيحييولد
@DataGetters, Setters, toString, equals, hashCode
@NoArgsConstructorمنشئ فارغ
@AllArgsConstructorمنشئ مع جميع الحقول
@Getterفقط getters
@Setterفقط setters

الاختبار​

  1. إعادة الترجمة
  2. التحقق من أن التطبيق يعمل
  3. يتم توليد الطرق عند الترجمة

7. تمرين 4: إعادة الإنشاء التلقائي للقاعدة​

البيان​

قم بتكوين التطبيق ليعيد إنشاء قاعدة البيانات تلقائيًا عند كل بدء تشغيل.

الهدف​

فهم استراتيجيات إدارة المخطط المختلفة.

التعليمات​

في application.properties (أو application.yml):

spring.jpa.hibernate.ddl-auto=create-drop

أو

spring:
jpa:
hibernate:
ddl-auto: create-drop

مقارنة الاستراتيجيات​

الاستراتيجيةالسلوكالاستخدام
noneلا يوجد إجراءالإنتاج
validateالتحقق من المخطط دون تعديلالإنتاج
updateتحديث المخططالتطوير
createإنشاء المخطط عند البدءالاختبارات
create-dropإنشاء عند البدء، حذف عند الإيقافالاختبارات الوحدوية

الانتباه​

create-drop يحذف جميع البيانات عند إيقاف التطبيق!

الاستخدام الموصى به:

# التطوير
spring:
jpa:
hibernate:
ddl-auto: update

# الإنتاج
spring:
jpa:
hibernate:
ddl-auto: validate

8. تمرين 5: الانتقال إلى MySQL، SQL Server أو Oracle​

البيان​

أعد تنفيذ الدمج الكامل مع قاعدة بيانات أخرى.

اختيار قاعدة البيانات​

الخيار أ: MySQL​

التبعية في pom.xml:

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

تكوين application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/greeting_db?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update

إنشاء القاعدة في MySQL:

CREATE DATABASE greeting_db;
CREATE USER 'root'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON greeting_db.* TO 'root'@'localhost';
FLUSH PRIVILEGES;

الخيار ب: SQL Server​

التبعية في pom.xml:

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>

تكوين application.properties:

spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=greeting_db
spring.datasource.username=sa
spring.datasource.password=YourStrong@Password
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto=update

إنشاء القاعدة في SQL Server:

CREATE DATABASE greeting_db;
GO

ALTER LOGIN sa WITH PASSWORD = 'YourStrong@Password';
GO

USE greeting_db;
GO

الخيار ج: Oracle​

التبعية في pom.xml:

<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>

تكوين application.properties:

spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.ddl-auto=update

إنشاء القاعدة في Oracle:

CREATE USER greeting_user IDENTIFIED BY oracle;
GRANT ALL PRIVILEGES TO greeting_user;

جدول المقارنة​

قاعدة البياناتالمنفذ الافتراضيلهجة Hibernateالصعوبة
PostgreSQL5432PostgreSQLDialectسهل
MySQL3306MySQLDialectسهل
SQL Server1433SQLServerDialectمتوسط
Oracle1521Oracle12cDialectصعب

التعليمات العامة​

  1. تثبيت قاعدة البيانات المختارة
  2. تعديل pom.xml مع برنامج التشغيل الصحيح
  3. تعديل application.properties مع التكوين الصحيح
  4. إنشاء قاعدة البيانات والمستخدم
  5. إعادة الترجمة والاختبار

حل المشاكل​

المشكلة 1: Connection Refused​

الأعراض:

org.postgresql.util.PSQLException: Connection to localhost:5432 refused

الحلول:

  1. التحقق من أن PostgreSQL قيد التشغيل:

Windows:

# Dans Services, chercher PostgreSQL
services.msc

Linux:

sudo systemctl status postgresql
sudo systemctl start postgresql
  1. التحقق من المنفذ في postgresql.conf

المشكلة 2: Authentication Failed​

الأعراض:

PSQLException: FATAL: password authentication failed for user "postgres"

الحلول:

  1. التحقق من كلمة المرور في application.properties
  2. إعادة تعيين كلمة المرور:
ALTER USER postgres WITH PASSWORD 'nouveau_mot_de_passe';

المشكلة 3: Database Does Not Exist​

الأعراض:

PSQLException: FATAL: database "greeting_db" does not exist

الحل:

إنشاء قاعدة البيانات:

CREATE DATABASE greeting_db;

المشكلة 4: Table Already Exists​

الأعراض:

Table "greeting" already exists

الحل:

تغيير استراتيجية DDL:

spring.jpa.hibernate.ddl-auto=update

المشكلة 5: Lombok Ne Fonctionne Pas​

الأعراض:

error: cannot find symbol
method getId()

الحلول:

  1. تثبيت ملحق Lombok في VS Code
  2. إعادة تشغيل VS Code
  3. التحقق من أن التبعية موجودة في pom.xml
  4. إعادة الترجمة: mvn clean install -DskipTests spring-boot:run

أسئلة المراجعة​

  1. ما هو الفرق بين Jakarta و Javax Persistence؟

  2. ماذا تفعل التعليمة @Entity؟

  3. ما هو الفرق بين create و update و validate لـ ddl-auto؟

  4. كيف يقوم Spring Data JPA بتوليد تنفيذ المستودع؟

  5. ماذا تفعل التعليمة @GeneratedValue(strategy = GenerationType.IDENTITY)؟

  6. لماذا يجب أن يكون لدينا منشئ فارغ في كيان JPA؟

  7. ماذا تولد التعليمة @Data من Lombok؟

  8. ما هو التكوين المستخدم في الإنتاج لـ ddl-auto؟


التسليمات​

يجب تسليم:

  1. الكود المصدري الكامل مع PostgreSQL يعمل
  2. لقطات شاشة تظهر:
    • التطبيق قيد التشغيل
    • البيانات في pgAdmin
    • استعلامات SQL في وحدة التحكم
  3. إكمال على الأقل 2 من التمارين
  4. وثيقة إجابات لأسئلة المراجعة

معايير التقييم​

المعيارالنقاط
دمج PostgreSQL يعمل25%
كيان Greeting معلق بشكل صحيح20%
المستودع تم إنشاؤه واستخدامه15%
البيانات محفوظة في PostgreSQL20%
التمارين مكتملة10%
أسئلة المراجعة مجاب عنها10%
المجموع100%

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

في العمل العملي القادم، سنرى:

  • TP3: إنشاء API CRUD كامل
  • العمليات: إنشاء، قراءة، تحديث، حذف
  • إدارة الأخطاء
  • التحقق من البيانات

الموارد الإضافية​

الوثائق​

الدروس​


العودة إلى الوحدة 2