ORM للغة Go (Golang): مقارنة وأمثلة
عند التطوير بلغة Go (Golang)، يعد العمل مع قواعد البيانات من المهام الأساسية. على عكس العديد من اللغات الديناميكية، لا تحتوي Go على ORM (تعيين الكائنات العلائقية) مدمج، لكن المجتمع أنشأ العديد من المكتبات القوية. في هذه المقالة، سنقارن بالتفصيل ثلاثة مناهج شائعة: GORM و Ent و sqlx. ستتعرف على نقاط القوة والضعف لكل منها، وسترى أيضًا أمثلة كود لكل مكتبة.
لماذا نحتاج ORM في Go؟
يعمل ORM على تبسيط التفاعل مع قواعد البيانات العلائقية، مما يسمح بالتعامل مع الجداول كهياكل Go عادية. المزايا الرئيسية:
- توليد تلقائي لاستعلامات SQL
- ترحيل مخطط قاعدة البيانات
- الحماية من حقن SQL
- تسريع التطوير
ومع ذلك، قد يقلل ORM من الأداء في الاستعلامات المعقدة ويخفي تفاصيل SQL، وهو أمر بالغ الأهمية للمشاريع عالية التحميل.
1. GORM — أشهر ORM للغة Go
GORM هو ORM كامل الوظائف مع مجتمع نشط (أكثر من 35 ألف نجمة على GitHub). يوفر واجهة برمجة تطبيقات غنية لعمليات CRUD والترحيل والخطافات والعلاقات.
مثال على العمل مع GORM
لنحدد نموذجًا وننفذ عمليات بسيطة:
package main
import ( "gorm.io/driver/sqlite" "gorm.io/gorm")
type User struct { ID uint `gorm:"primaryKey"` Name string Email string `gorm:"uniqueIndex"`}
func main() { db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") }
// الترحيل التلقائي db.AutoMigrate(&User{})
// إنشاء db.Create(&User{Name: "Alice", Email: "alice@example.com"})
// قراءة var user User db.First(&user, 1) // حسب المعرف db.First(&user, "email = ?", "alice@example.com")
// تحديث db.Model(&user).Update("Name", "Alice Updated")
// حذف db.Delete(&user)}إيجابيات GORM:
- واجهة برمجة تطبيقات بسيطة وبديهية
- دعم العلاقات (BelongsTo, HasMany, Many2Many)
- الترحيل والخطافات المضمنة
- دعم واسع لأنظمة إدارة قواعد البيانات (PostgreSQL, MySQL, SQLite, SQL Server)
سلبيات:
- انخفاض الأداء في الاستعلامات المعقدة
- سلوك سحري (مثل التغيير التلقائي للحقول)
- صعوبات مع استعلامات SQL المخصصة
2. Ent — ORM قائم على الكود من Facebook
Ent هو ORM يعتمد على توليد الكود. بدلاً من الانعكاس (Reflection) كما في GORM، يقوم Ent بتوليد كود Go مقيد الأنواع من المخطط، مما يضمن أداءً عاليًا وأمانًا للأنواع.
مثال على العمل مع Ent
أولاً، نحدد المخطط في ملف schema/user.go:
package schema
import ( "entgo.io/ent" "entgo.io/ent/schema/field")
type User struct { ent.Schema}
func (User) Fields() []ent.Field { return []ent.Field{ field.String("name"), field.String("email").Unique(), }}بعد توليد الكود (الأمر go generate ./...) نستخدم واجهة API المُولَّدة:
package main
import ( "context" "log"
"your-project/ent" _ "github.com/mattn/go-sqlite3")
func main() { client, err := ent.Open("sqlite3", "file:ent.db?cache=shared&_fk=1") if err != nil { log.Fatal(err) } defer client.Close()
ctx := context.Background()
// الترحيل التلقائي if err := client.Schema.Create(ctx); err != nil { log.Fatal(err) }
// إنشاء مستخدم user, err := client.User.Create(). SetName("Bob"). SetEmail("bob@example.com"). Save(ctx) if err != ni