Goku

آموزش کامل، پیشرفته و عمیق GORM در Go

این جزوه به بررسی جامع و تخصصی GORM، کتابخانه ORM محبوب برای Go، می‌پردازد. GORM به دلیل انعطاف‌پذیری، پشتیبانی از دیتابیس‌های مختلف، و قابلیت‌های پیشرفته، یکی از بهترین ابزارها برای مدیریت دیتابیس در Go است. تمام موضوعات درخواستی با جزئیات کامل، مثال‌های عملی، و نکات پیشرفته شرح داده شده‌اند.


1. مفاهیم پایه GORM (به‌صورت حرفه‌ای)

1.1. مدل‌سازی دقیق دیتابیس

مدل‌ها در GORM به صورت struct تعریف می‌شوند و با استفاده از برچسب‌ها (tags) به جداول دیتابیس نگاشت می‌شوند.

مثال مدل پیشرفته

package models

import (
    "time"
    "gorm.io/gorm"
)

// User مدل کاربر با فیلدهای مختلف
type User struct {
    ID        uint           `gorm:"primaryKey"`
    Name      string         `gorm:"type:varchar(100);not null"`
    Email     string         `gorm:"type:varchar(100);uniqueIndex"`
    CreatedAt time.Time      `gorm:"autoCreateTime"`
    UpdatedAt time.Time      `gorm:"autoUpdateTime"`
    DeletedAt gorm.DeletedAt `gorm:"index"` // برای Soft Delete
}

1.2. تعریف انواع فیلدها

GORM از انواع داده‌های سفارشی، JSON، و Enum پشتیبانی می‌کند.

Custom Type

package models

import (
    "database/sql/driver"
    "encoding/json"
    "errors"
    "gorm.io/gorm"
)

type JSONMap map[string]interface{}

// Value برای تبدیل به دیتابیس
func (j JSONMap) Value() (driver.Value, error) {
    return json.Marshal(j)
}

// Scan برای خواندن از دیتابیس
func (j *JSONMap) Scan(value interface{}) error {
    bytes, ok := value.([]byte)
    if !ok {
        return errors.New("type assertion to []byte failed")
    }
    return json.Unmarshal(bytes, j)
}

type Profile struct {
    ID       uint     `gorm:"primaryKey"`
    UserID   uint     `gorm:"index"`
    Settings JSONMap  `gorm:"type:jsonb"` // برای Postgres
}

Enum

package models

import "gorm.io/gorm"

type Role string

const (
    Admin  Role = "admin"
    User   Role = "user"
    Guest  Role = "guest"
)

type User struct {
    ID   uint `gorm:"primaryKey"`
    Role Role `gorm:"type:varchar(20);default:'user'"`
}

1.3. استفاده از برچسب‌ها

برچسب‌ها (tags) برای کنترل دقیق رفتار مدل‌ها استفاده می‌شوند.

مثال

type Order struct {
    ID         uint   `gorm:"primaryKey"`
    UserID     uint   `gorm:"index;foreignKey:UserID;references:ID"` // کلید خارجی
    Amount     int    `gorm:"not null;check:amount >= 0"`           // شرط
    Status     string `gorm:"type:varchar(50);default:'pending'"`
    UniqueCode string `gorm:"uniqueIndex:idx_code"`                 // ایندکس منحصربه‌فرد
}

1.4. Embedded Structs و Inheritance

GORM از structهای تو در تو برای مدل‌سازی روابط پیچیده پشتیبانی می‌کند.

مثال

type BaseModel struct {
    CreatedAt time.Time      `gorm:"autoCreateTime"`
    UpdatedAt time.Time      `gorm:"autoUpdateTime"`
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

type User struct {
    BaseModel
    ID    uint   `gorm:"primaryKey"`
    Name  string `gorm:"type:varchar(100)"`
}

type Admin struct {
    User
    Permissions JSONMap `gorm:"type:jsonb"`
}

1.5. نکات پیشرفته

1.6. خطاهای رایج


2. عملیات پیشرفته CRUD

2.1. Query پیشرفته با Conditions پیچیده

GORM از شرط‌های پیچیده با روش‌های مختلف پشتیبانی می‌کند.

مثال

var users []User
db.Where("name LIKE ? AND role = ?", "%Ali%", "admin").
   Or("email LIKE ?", "%@example.com").
   Find(&users)

2.2. Dynamic Filters و Query Builder

برای ساخت کوئری‌های پویا از Scopes استفاده کنید.

مثال

func WithName(name string) func(*gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if name != "" {
            return db.Where("name LIKE ?", "%"+name+"%")
        }
        return db
    }
}

func WithRole(role string) func(*gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if role != "" {
            return db.Where("role = ?", role)
        }
        return db
    }
}

var users []User
db.Scopes(WithName("Ali"), WithRole("admin")).Find(&users)

2.3. کار با Preload و Joins

Preload

برای بارگذاری روابط:

type User struct {
    ID      uint
    Name    string
    Orders  []Order `gorm:"foreignKey:UserID"`
}

var user User
db.Preload("Orders").First(&user, 1)

Joins

برای کوئری‌های پیچیده:

var results []struct {
    UserName  string
    OrderID   uint
}
db.Model(&User{}).
   Select("users.name AS user_name, orders.id AS order_id").
   Joins("LEFT JOIN orders ON orders.user_id = users.id").
   Scan(&results)

2.4. Bulk Insert و Batch Processing

Bulk Insert

users := []User{
    {Name: "Ali", Email: "ali@example.com"},
    {Name: "Bob", Email: "bob@example.com"},
}
db.CreateInBatches(users, 100) // دسته‌های 100 تایی

Batch Processing

db.Where("status = ?", "pending").FindInBatches(&users, 100, func(tx *gorm.DB, batch int) error {
    for i := range users {
        users[i].Status = "processed"
    }
    return tx.Save(&users).Error
})

2.5. کار با Soft Delete

GORM به طور داخلی از Soft Delete پشتیبانی می‌کند.

// حذف نرم
db.Delete(&user)

// بازیابی با حذف‌شده‌ها
db.Unscoped().Find(&users)

// حذف دائم
db.Unscoped().Delete(&user)

2.6. Transactions، SavePoints و Rollback

Transaction

err := db.Transaction(func(tx *gorm.DB) error {
    if err := tx.Create(&User{Name: "Ali"}).Error; err != nil {
        return err
    }
    if err := tx.Create(&Order{UserID: 1, Amount: 100}).Error; err != nil {
        return err
    }
    return nil
})

SavePoints

db.Transaction(func(tx *gorm.DB) error {
    tx.Create(&User{Name: "Ali"})
    tx.SavePoint("sp1")
    tx.Create(&Order{UserID: 1, Amount: 100})
    if someCondition {
        tx.RollbackTo("sp1")
    }
    return nil
})

2.7. نکات پیشرفته

2.8. خطاهای رایج


3. معماری تمیز و استفاده از SOLID در لایه دیتابیس

3.1. Repository Pattern

Repository Pattern لایه‌ای بین منطق کسب‌وکار و دیتابیس ایجاد می‌کند.

مثال

3.2. Interface Segregation و Dependency Injection

3.3. جداسازی لایه مدل

برای تست‌پذیری، مدل‌ها را از دیتابیس جدا کنید:

package models

type User struct {
    ID   uint
    Name string
}

// Validate برای اعتبارسنجی
func (u *User) Validate() error {
    if u.Name == "" {
        return errors.New("name is required")
    }
    return nil
}

3.4. نکات پیشرفته

3.5. خطاهای رایج


4. مدیریت پیشرفته مهاجرت (Migrations)

4.1. AutoMigrate vs Manual Migrations

golang-migrate

4.2. نسخه‌بندی دیتابیس و CI/CD

4.3. نکات پیشرفته

4.4. خطاهای رایج


5. کار با دیتابیس‌های مختلف

5.1. استفاده از ویژگی‌های خاص هر دیتابیس

5.2. Connection Pooling، Timeouts، و Failover

5.3. پشتیبانی از Replication و Sharding

5.4. نکات پیشرفته

5.5. خطاهای رایج


6. بهینه‌سازی و Performance Tuning

6.1. Index Optimization

6.2. Explain Analyze روی Queryهای GORM

6.3. Logger سفارشی

type CustomLogger struct {
    logger.Logger
}

func (l *CustomLogger) LogMode(level logger.LogLevel) logger.Interface {
    newLogger := *l
    newLogger.Logger = l.Logger.LogMode(level)
    return &newLogger
}

func (l *CustomLogger) Info(ctx context.Context, msg string, data ...interface{}) {
    log.Printf("[GORM] %s %v", msg, data)
}

db, _ := gorm.Open(postgres.Open("..."), &gorm.Config{
    Logger: &CustomLogger{},
})

6.4. کش‌کردن Queryها

6.5. Query Profiling و Monitoring

6.6. نکات پیشرفته

6.7. خطاهای رایج


7. امنیت دیتابیس در GORM

7.1. جلوگیری از SQL Injection

همیشه از پارامترهای باندشده استفاده کنید:

db.Where("name = ?", userInput).Find(&users) // ایمن
db.Raw("SELECT * FROM users WHERE name = '" + userInput + "'") // ناایمن

7.2. دسترسی‌ها و Authorization

7.3. Audit Trail و History Logs

type AuditLog struct {
    ID        uint
    TableName string
    RecordID  uint
    Action    string
    Data      JSONMap `gorm:"type:jsonb"`
    CreatedAt time.Time
}

func LogAudit(db *gorm.DB, table string, recordID uint, action string, data interface{}) error {
    logEntry := AuditLog{
        TableName: table,
        RecordID:  recordID,
        Action:    action,
        Data:      JSONMap(data),
        CreatedAt: time.Now(),
    }
    return db.Create(&logEntry).Error
}

7.4. نکات پیشرفته

7.5. خطاهای رایج


8. تست‌نویسی و Mock کردن دیتابیس

8.1. Unit Test با Mock GORM

استفاده از پکیج go-sqlmock:

package repository

import (
    "testing"
    "github.com/DATA-DOG/go-sqlmock"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

func TestGORMUserRepository_FindByID(t *testing.T) {
    db, mock, _ := sqlmock.New()
    gormDB, _ := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})
    repo := NewGORMUserRepository(gormDB)

    mock.ExpectQuery(`SELECT * FROM "users" WHERE id = \$1`).
        WithArgs(1).
        WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "Ali"))

    user, err := repo.FindByID(1)
    if err != nil || user.Name != "Ali" {
        t.Errorf("Expected user Ali, got %v", user)
    }
}

8.2. Integration Test با دیتابیس واقعی و Docker

8.3. تست Migration

func TestMigration(t *testing.T) {
    db, _ := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
    m, _ := migrate.New("file://migrations", "sqlite://test.db")
    m.Up()
    db.AutoMigrate(&User{})
    // تست مدل‌ها
}

8.4. نکات پیشرفته

8.5. خطاهای رایج


9. مستندسازی و نگهداری

9.1. نوشتن کامنت‌های دقیق

// Package models defines database models for the application.
// All models include standard fields for auditing and soft deletion.
package models

// User represents a user in the system.
// It includes fields for identification, authentication, and auditing.
type User struct {
    ID        uint           `gorm:"primaryKey" json:"id"`
    Name      string         `gorm:"type:varchar(100);not null" json:"name"`
    Email     string         `gorm:"type:varchar(100);uniqueIndex" json:"email"`
    CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
    UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // Hidden in JSON
}

9.2. استفاده از godoc

9.3. تولید ERD

استفاده از ابزارهایی مثل dbml یا pgAdmin:

9.4. نکات پیشرفته

9.5. خطاهای رایج


10. انجمن و منابع پیشرفته یادگیری

10.1. مشارکت در پروژه‌های متن‌باز

10.2. مطالعه کد پروژه‌های معروف

10.3. منابع یادگیری

10.4. نکات پیشرفته

10.5. خطاهای رایج


11. بهترین شیوه‌ها و نکات کلی


12. نتیجه‌گیری

این جزوه تمام جنبه‌های GORM را به صورت پیشرفته و عمیق پوشش داد. از مدل‌سازی حرفه‌ای و عملیات CRUD پیشرفته تا معماری تمیز، مهاجرت‌ها، بهینه‌سازی، امنیت، تست‌نویسی، مستندسازی، و مشارکت در انجمن، هر بخش با مثال‌های عملی و نکات تخصصی ارائه شد. برای یادگیری عمیق‌تر: