GORM для Go: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое GORM и зачем он нужен?

GORM — это мощная и популярная ORM (Object-Relational Mapping) библиотека для языка Go. Она предоставляет разработчикам удобный способ взаимодействия с реляционными базами данных (PostgreSQL, MySQL, SQLite, SQL Server и др.), используя объектно-ориентированный подход на Go.

Зачем нужен GORM?

  • Автоматизация CRUD: GORM берет на себя написание шаблонного SQL-кода для создания, чтения, обновления и удаления записей.
  • Миграции схемы: Позволяет автоматически создавать и обновлять таблицы в базе данных на основе ваших Go-структур.
  • Связи между таблицами: Упрощает работу с отношениями "один ко многим", "многие ко многим" и "один к одному".
  • Безопасность: Встроенная защита от SQL-инъекций через параметризованные запросы.
  • Производительность: Поддерживает ленивую загрузку (Lazy Loading), предзагрузку (Eager Loading) и кэширование.
  • Сообщество: Огромное сообщество, множество плагинов и расширений.

Установка GORM

GORM устанавливается стандартным способом для Go-пакетов. Убедитесь, что у вас установлен Go (версии 1.18+).

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite  # Для SQLite
go get -u gorm.io/driver/mysql   # Для MySQL
go get -u gorm.io/driver/postgres # Для PostgreSQL
go get -u gorm.io/driver/sqlserver # Для SQL Server

Примечание: В отличие от Python (pip) или Node.js (npm), в Go нет единого менеджера пакетов. Команда go get загружает исходный код и компилирует его в ваш проект.

Быстрый старт с примером кода

Создадим простое приложение для управления пользователями с использованием SQLite.

package main

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

// User — модель данных, соответствующая таблице в БД
type User struct {
    ID    uint   `gorm:"primaryKey"`
    Name  string `gorm:"size:100;not null"`
    Email string `gorm:"uniqueIndex;size:255"`
    Age   int
}

func main() {
    // 1. Подключение к базе данных
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // 2. Автомиграция — создание таблицы
    db.AutoMigrate(&User{})

    // 3. Создание записи (Create)
    user := User{Name: "Иван Петров", Email: "ivan@example.com", Age: 30}
    result := db.Create(&user)
    if result.Error != nil {
        fmt.Println("Ошибка создания:", result.Error)
    }
    fmt.Printf("Создан пользователь с ID: %d\n", user.ID)

    // 4. Чтение записи (Read)
    var readUser User
    db.First(&readUser, user.ID) // Поиск по первичному ключу
    fmt.Printf("Найден пользователь: %+v\n", readUser)

    // 5. Обновление записи (Update)
    db.Model(&readUser).Update("Age", 31)
    fmt.Printf("Обновленный возраст: %d\n", readUser.Age)

    // 6. Удаление записи (Delete)
    db.Delete(&readUser)
    fmt.Println("Пользователь удален")
}

Основные методы/функции GORM

1. gorm.Open(dialector, config)

Что делает: Устанавливает соединение с базой данных. Возвращает экземпляр *gorm.DB.

Параметры:

  • dialector — драйвер базы данных (например, sqlite.Open, mysql.Open, postgres.Open).
  • config — конфигурация GORM (gorm.Config).

Пример:

db, err := gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})

2. db.AutoMigrate(models ...interface{})

Что делает: Автоматически создает или обновляет таблицы в базе данных на основе переданных структур. Добавляет недостающие столбцы, индексы, ограничения.

Параметры:

  • models — указатели на структуры (например, &User{}, &Product{}).

Пример:

db.AutoMigrate(&User{}, &Order{})

3. db.Create(value)

Что делает: Вставляет новую запись в таблицу. Возвращает объект *gorm.DB с результатом.

Параметры:

  • value — указатель на структуру или срез структур.

Пример:

user := User{Name: "Анна", Email: "anna@test.com"}
result := db.Create(&user)
fmt.Println(result.RowsAffected) // 1
fmt.Println(user.ID) // ID после вставки

4. db.First(dest, conds ...interface{})

Что делает: Находит первую запись, удовлетворяющую условиям, и сохраняет её в dest. Если запись не найдена, возвращает ошибку ErrRecordNotFound.

Параметры:

  • dest — указатель на структуру.
  • conds — условия поиска (ID, строка WHERE, map).

Пример:

var user User
db.First(&user, 10) // Поиск по ID = 10
db.First(&user, "email = ?", "test@example.com") // Поиск по email

5. db.Find(dest, conds ...interface{})

Что делает: Находит все записи, удовлетворяющие условиям. Результат сохраняется в срез структур.

Параметры:

  • dest — указатель на срез структур.
  • conds — условия поиска.

Пример:

var users []User
db.Find(&users) // Все пользователи
db.Find(&users, "age > ?", 25) // Пользователи старше 25 лет

6. db.Model(value)

Что делает: Указывает модель, с которой будут выполняться последующие операции (Update, Where, Select и т.д.).

Параметры:

  • value — указатель на структуру.

Пример:

db.Model(&User{}).Where("age > ?", 18).Update("status", "active")

7. db.Where(query, args ...interface{})

Что делает: Добавляет условие WHERE к запросу. Поддерживает строки, map и структуры.

Параметры:

  • query — строка SQL с плейсхолдерами (?) или map/структура.
  • args — значения для плейсхолдеров.

Пример:

var users []User
db.Where("name LIKE ?", "%Иван%").Find(&users)
db.Where(map[string]interface{}{"age": 30, "status": "active"}).Find(&users)

8. db.Order(value)

Что делает: Добавляет сортировку ORDER BY к запросу.

Параметры:

  • value — строка с направлением сортировки.

Пример:

db.Order("age DESC, name ASC").Find(&users)

9. db.Limit(limit) и db.Offset(offset)

Что делает: Ограничивает количество возвращаемых записей (LIMIT) и пропускает указанное количество записей (OFFSET). Используется для пагинации.

Параметры:

  • limit — максимальное количество записей.
  • offset — количество пропускаемых записей.

Пример:

db.Limit(10).Offset(20).Find(&users) // Страница 3 по 10 записей

10. db.Preload(query, args ...interface{})

Что делает: Выполняет предзагрузку (Eager Loading) связанных моделей. Позволяет загрузить связанные данные одним запросом (или несколькими, но эффективно).

Параметры:

  • query — имя поля связи в структуре.
  • args — дополнительные условия для связанной модели.

Пример:

type Order struct {
    gorm.Model
    UserID uint
    User   User
}
var orders []Order
db.Preload("User").Find(&orders) // Загружает заказы вместе с пользователями

11. db.Delete(value, conds ...interface{})

Что делает: Удаляет запись (мягкое удаление, если модель имеет поле DeletedAt).

Параметры:

  • value — указатель на структуру или условие.
  • conds — дополнительные условия.

Пример:

db.Delete(&User{}, 10) // Удалить пользователя с ID 10
db.Where("age < ?", 18).Delete(&User{}) // Удалить всех несовершеннолетних

12. db.Raw(sql, values ...interface{})

Что делает: Выполняет произвольный SQL-запрос. Полезно для сложных запросов, которые не поддерживаются GORM напрямую.

Параметры:

  • sql — строка SQL-запроса.
  • values — значения для плейсхолдеров.

Пример:

type Result struct {
    Name  string
    Total int
}
var results []Result
db.Raw("SELECT name, COUNT(*) as total FROM users GROUP BY name").Scan(&results)

Реальный практический пример использования

Создадим REST API для интернет-магазина с использованием GORM и стандартной библиотеки net/http.

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

// Product — модель товара
type Product struct {
    ID          uint    `gorm:"primaryKey" json:"id"`
    Name        string  `gorm:"size:200;not null" json:"name"`
    Price       float64 `gorm:"not null" json:"price"`
    Description string  `gorm:"type:text" json:"description"`
    Stock       int     `gorm:"default:0" json:"stock"`
}

var db *gorm.DB

func main() {
    var err error
    db, err = gorm.Open(sqlite.Open("shop.db"), &gorm.Config{})
    if err != nil {
        log.Fatal("Ошибка подключения к БД:", err)
    }
    db.AutoMigrate(&Product{})

    http.HandleFunc("/products", productsHandler)
    http.HandleFunc("/products/", productHandler)

    log.Println("Сервер запущен на :8080")
    log.Fatal(http.ListenAndServe                    

Рекомендаций