Użyj GORM z go-mssqldb

GORM to popularny ORM dla Go, który obsługuje SQL Server za pośrednictwem dialektu gorm.io/driver/sqlserver, wewnętrznie korzystającego z go-mssqldb. GORM obsługuje różnice w schematach, generowanie zapytań i mapowanie typów, dzięki czemu możesz pracować ze strukturami Go zamiast pisać surowy SQL.

Ten artykuł omawia wzorce GORM specyficzne dla SQL Server. Dla ogólnego zastosowania GORM zobacz dokumentację GORM.

Przykłady w tym artykule porównane są z przykładową bazą danych AdventureWorks2025 .

Zainstaluj GORM i dialekt SQL Server

Stwórz lub otwórz moduł Go, a następnie dodaj GORM i dialekt SQL Server:

mkdir go-gorm-sqlserver
cd go-gorm-sqlserver
go mod init example.com/go-gorm-sqlserver
go get gorm.io/gorm
go get gorm.io/driver/sqlserver

Nawiązywanie połączenia z programem SQL Server

Otwórz sesję GORM, przekazując do dialektu parametr DSN sqlserver://:

package main

import (
    "fmt"
    "log"

    "gorm.io/driver/sqlserver"
    "gorm.io/gorm"
)

func main() {
    // Replace <server>, <user>, and <password> with your own values.
    // In production, load credentials from environment variables or a secret store.
    dsn := "sqlserver://<user>:<password>@<server>.database.windows.net:1433?database=AdventureWorks2025&encrypt=true&TrustServerCertificate=false"
    db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }

    sqlDB, err := db.DB()
    if err != nil {
        log.Fatal(err)
    }
    defer sqlDB.Close()

    fmt.Println("Connected to SQL Server with GORM")
}

Dialekt korzysta wewnętrznie ze sterownika go-mssqldb, więc wszystkie opcje połączenia z Parametry połączenia i Opcje połączenia mają zastosowanie do DSN.

Przypisz modele do istniejących tabel

Domyślna konwencja nazewnictwa w GORM używa nazw tabel pisanych małymi literami w liczbie mnogiej (na przykład products), co nie jest zgodne z konwencjami stosowanymi w SQL Server. Użyj TableName() metody do powiązania modelu z istniejącą tabelą kwalifikowaną przez schemat:

type Product struct {
    ProductID   int32  `gorm:"column:ProductID;primaryKey"`
    Name        string `gorm:"column:Name"`
    ProductLine *string `gorm:"column:ProductLine"`
    ListPrice   float64 `gorm:"column:ListPrice"`
}

func (Product) TableName() string {
    return "Production.Product"
}

Wskazówka

Zawsze określajTableName(), kiedy pracujesz z istniejącymi bazami SQL Server. Poleganie na automatycznym tworzeniu liczby mnogiej przez GORM powoduje błędy „invalid object name” w przypadku tabel zgodnych z konwencjami nazewnictwa SQL Server.

Wyszukaj dane

Po zdefiniowaniu modelu użyj metod zapytań GORM do odczytu danych:

Znajdź wiersze za pomocą klauzuli WHERE

Filtruj, sortuj i ograniczaj wyniki za pomocą Where, Order, oraz Limit:

var products []Product
result := db.Where("ListPrice > ?", 1000).
    Order("ListPrice DESC").
    Limit(5).
    Find(&products)
if result.Error != nil {
    log.Fatal(result.Error)
}

for _, p := range products {
    fmt.Printf("%-35s $%.2f\n", p.Name, p.ListPrice)
}

Pobierz pojedynczy wiersz

Użyj First z warunkiem ładowania jednego rekordu:

var product Product
if err := db.First(&product, "ProductID = ?", 680).Error; err != nil {
    log.Fatal(err)
}
fmt.Printf("%s: $%.2f\n", product.Name, product.ListPrice)

Zlicz wiersze

Użyj Count w zakresie modelu, aby uzyskać liczbę wierszy:

var count int64
db.Model(&Product{}).Where("ListPrice > ?", 0).Count(&count)
fmt.Printf("Products with a list price: %d\n", count)

Wstawiaj, aktualizuj i usuwaj wiersze

Operacje zapisu wykorzystują te same struktury modelu. Przykłady w tej sekcji są skierowane na Production.ScrapReason, dzięki czemu nie modyfikują kluczowych tabel.

type ScrapReason struct {
    ScrapReasonID int16     `gorm:"column:ScrapReasonID;primaryKey;autoIncrement"`
    Name          string    `gorm:"column:Name"`
    ModifiedDate  time.Time `gorm:"column:ModifiedDate"`
}

func (ScrapReason) TableName() string {
    return "Production.ScrapReason"
}

Wstaw wiersz

Przekaż do Create strukturę z wypełnionymi polami, aby wstawić wiersz i uzupełnić automatycznie wygenerowany klucz podstawowy:

reason := ScrapReason{Name: "GORM test scrap", ModifiedDate: time.Now()}
if err := db.Create(&reason).Error; err != nil {
    log.Fatal(err)
}
fmt.Printf("Inserted ScrapReasonID: %d\n", reason.ScrapReasonID)

// Clean up
db.Delete(&reason)

Zaktualizuj wiersz

Użyj Model z Update, aby zmienić wartość kolumny:

reason := ScrapReason{Name: "GORM update test", ModifiedDate: time.Now()}
db.Create(&reason)

db.Model(&reason).Update("Name", "GORM updated scrap")

var updated ScrapReason
db.First(&updated, reason.ScrapReasonID)
fmt.Printf("Updated name: %s\n", updated.Name)

// Clean up
db.Delete(&reason)

Usuwanie wiersza

Przekaż strukturę z zasiedlonym kluczem głównym do Delete:

reason := ScrapReason{Name: "GORM delete test", ModifiedDate: time.Now()}
db.Create(&reason)
id := reason.ScrapReasonID

db.Delete(&reason)

var count int64
db.Model(&ScrapReason{}).Where("ScrapReasonID = ?", id).Count(&count)
fmt.Printf("Rows remaining with that ID: %d\n", count)

Wstawka wsadowa i limit 2100 parametrów

SQL Server ogranicza liczbę parametrów w instrukcji sp_executesql do 2 100. Wstawka wsadowa w GORM generuje klauzulę wielowierszową VALUES , gdzie każda kolumna zajmuje jeden parametr, więc tabela z 10 kolumnami osiąga limit około 210 wierszy na partię.

Ustaw CreateBatchSize dla tej sesji, aby pozostać w limicie:

reasons := make([]ScrapReason, 50)
for i := range reasons {
    reasons[i] = ScrapReason{
        Name:         fmt.Sprintf("Batch scrap %d", i+1),
        ModifiedDate: time.Now(),
    }
}

// 2 inserted columns per row (Name, ModifiedDate; the autoIncrement ID is excluded),
// so batches of 500 are well within the 2,100-parameter limit.
result := db.CreateInBatches(&reasons, 500)
if result.Error != nil {
    log.Fatal(result.Error)
}
fmt.Printf("Inserted %d rows in batches\n", result.RowsAffected)

// Clean up
for _, r := range reasons {
    db.Delete(&r)
}

Dla tabel z wieloma kolumnami proporcjonalnie zmniejszaj rozmiar partii. Bezpieczna formuła: batch size = floor(2100 / number of inserted columns).

Transactions

Opakuj wiele operacji w transakcji GORM:

err := db.Transaction(func(tx *gorm.DB) error {
    r1 := ScrapReason{Name: "TX scrap 1", ModifiedDate: time.Now()}
    if err := tx.Create(&r1).Error; err != nil {
        return err
    }

    r2 := ScrapReason{Name: "TX scrap 2", ModifiedDate: time.Now()}
    if err := tx.Create(&r2).Error; err != nil {
        return err
    }

    // Both inserts commit together.
    fmt.Printf("Inserted IDs: %d, %d\n", r1.ScrapReasonID, r2.ScrapReasonID)

    // Clean up inside the same transaction.
    tx.Delete(&r1)
    tx.Delete(&r2)

    return nil
})
if err != nil {
    log.Fatal(err)
}
fmt.Println("Transaction committed")

Jeśli callback zwraca błąd, GORM automatycznie wycofuje transakcję.

Awaryjne użycie surowego SQL

Gdy kreator zapytań GORM nie generuje potrzebnej składni SQL Server, przejdź do surowego SQL, nadal korzystając z sesji GORM:

var results []struct {
    Name      string
    ListPrice float64
}
result := db.Raw("SELECT TOP 5 Name, ListPrice FROM Production.Product WHERE ListPrice > @price ORDER BY ListPrice DESC",
    sql.Named("price", 1000)).Scan(&results)
if result.Error != nil {
    log.Fatal(result.Error)
}

for _, r := range results {
    fmt.Printf("%-35s $%.2f\n", r.Name, r.ListPrice)
}

Surowe zapytania korzystają z tej samej puli połączeń zarządzanej przez GORM, więc ustawienia puli z db.DB() nadal mają zastosowanie.

Konfiguruj pulę połączeń

Uzyskaj dostęp do bazowego elementu *sql.DB, aby ustawić parametry puli:

sqlDB, err := db.DB()
if err != nil {
    log.Fatal(err)
}
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(5 * time.Minute)

Wskazówki dotyczące określania rozmiaru puli można znaleźć w sekcji Buforowanie połączeń.

Rozważania dotyczące AutoMigrate

AutoMigrate Tworzy lub modyfikuje tabele, aby pasowały do definicji modelu. Działa z SQL Server, ale zwróć uwagę na następujące zachowania:

  • AutoMigrate tworzy tabele w domyślnym schemacie (dbo), chyba że poprzedzisz TableName() prefiksem schematu.
  • Dodaje brakujące kolumny i tworzy indeksy, ale nie usuwa kolumn ani nie zmienia istniejących typów kolumn.
  • SQL Server ma limit 128 znaków na nazwy indeksów. Generowane przez GORM nazwy indeksów mogą przekraczać ten limit dla tabel o długich nazwach lub indeksów złożonych. Używaj tagów jawnych gorm:"index:idx_short_name" , jeśli osiągniesz ten limit.

W przypadku baz produkcyjnych z zarządzanymi schematami preferuj narzędzia migracji zamiast AutoMigrate.