لماذا Go و PostgreSQL هما الثنائي المثالي؟
\n\nفي عالم تطوير الواجهة الخلفية، أثبت كل من Go (أو Golang) و PostgreSQL أنهما ثنائي قوي لبناء أنظمة عالية التحميل وموثوقة. يمنحنا Go أداءً لا يُصدق، وبساطة، ودعمًا ممتازًا للتزامن، بينما PostgreSQL هي قاعدة بيانات صناعية غنية بالميزات والموثوقية وقابلية التوسع. بدمجهما، تحصل على مجموعة قادرة على تحمل أعباء الشركات الناشئة ومتطلبات المشاريع المؤسسية.
\n\nفي هذه المقالة، لن نمر فقط على بناء الجملة. سنقوم بإنشاء دليل عملي كامل: من تثبيت المشغل إلى تحسين مجمع الاتصالات والعمل مع المعاملات. ستتعلم كيفية تجنب الأخطاء الشائعة وكتابة كود فعال بلغة Go للعمل مع PostgreSQL. هيا بنا!
\n\nالبدء: اختيار المشغل والاتصال
\n\nأول ما يجب فعله هو تثبيت المشغل. في عالم Go، هناك مرشحان رئيسيان:
\n- \n
- lib/pq — كلاسيكي، بسيط، لكنه لم يعد مدعومًا بنشاط. \n
- pgx — حديث، سريع، يتطور بنشاط، ويدعم المزيد من ميزات PostgreSQL (مثل COPY، LISTEN/NOTIFY، المصفوفات). أوصي به تحديدًا. \n
نقوم بتثبيت pgx:
\ngo get github.com/jackc/pgx/v5\n\nالآن لنكتب كود الاتصال بقاعدة البيانات. لننشئ ملف main.go:
package main\n\nimport (\n "context"\n "fmt"\n "log"\n\n "github.com/jackc/pgx/v5/pgxpool"\n)\n\nfunc main() {\n // رابط الاتصال: المستخدم، كلمة المرور، المضيف، المنفذ، اسم قاعدة البيانات\n dsn := "postgres://myuser:mypassword@localhost:5432/mydb?sslmode=disable"\n\n // إنشاء مجمع اتصالات (connection pool)\n pool, err := pgxpool.New(context.Background(), dsn)\n if err != nil {\n log.Fatalf("فشل إنشاء المجمع: %v", err)\n }\n defer pool.Close()\n\n // التحقق من الاتصال\n err = pool.Ping(context.Background())\n if err != nil {\n log.Fatalf("فشل الاتصال: %v", err)\n }\n\n fmt.Println("تم الاتصال بنجاح بـ PostgreSQL!")\n}\n\nلاحظ: نحن نستخدم pgxpool، وليس اتصالًا بسيطًا. مجمع الاتصالات هو أمر لا غنى عنه للإنتاج. يسمح بإعادة استخدام الاتصالات، وتجنب التكاليف الإضافية لإنشائها وإغلاقها.
عمليات CRUD: إنشاء، قراءة، تحديث، حذف
\n\nالآن دعنا ننفذ العمليات الكلاسيكية على جدول users. لنفترض أن لدينا الهيكل التالي:
type User struct {\n ID int\n Name string\n Age int\n}\n\n\nإنشاء الجدول (Create)
\nمن الأفضل تنفيذ الترحيلات باستخدام أدوات منفصلة، لكن كمثال سأعرض كيفية تنفيذ SQL خام:
\nfunc createTable(ctx context.Context, pool *pgxpool.Pool) error {\n query := `\n CREATE TABLE IF NOT EXISTS users (\n id SERIAL PRIMARY KEY,\n name TEXT NOT NULL,\n age INT NOT NULL\n );\n `\n _, err := pool.Exec(ctx, query)\n return err\n}\n\n\nإضافة سجل (Insert)
\nنستخدم QueryRow لإرجاع المعرف المُنشأ:
func createUser(ctx context.Context, pool *pgxpool.Pool, user *User) error {\n query := `INSERT INTO users (name, age) VALUES ($1, $2) RETURNING id`\n err := pool.QueryRow(ctx, query, user.Name, user.Age).Scan(&user.ID)\n return err\n}\n\n\nقراءة سجل (Select)
\nfunc getUserByID(ctx context.Context, pool *pgxpool.Pool, id int) (*User, error) {\n query := `SELECT id, name, age FROM users WHERE id = $1`\n user := &User{}\n err := pool.QueryRow(ctx, query, id).Scan(&user.ID, &user.Name, &user.Age)\n if err != nil {\n return nil, err\n }\n return user, nil\n}\n\n\n