логотип PurpleSchool
логотип PurpleSchool

Разбираемся с SQL в Golang

Автор

Александр Гольцман

Работа с базами данных — ключевая часть бэкенд-разработки. В языке программирования Go (или Golang) для взаимодействия с реляционными базами данных используется стандартная библиотека database/sql, а также сторонние ORM и библиотеки для удобной работы с SQL-запросами.

В этой статье я покажу, как подключаться к базе данных, выполнять SQL-запросы и разберу популярные инструменты для работы с SQL в Go. Вы узнаете, как работать с транзакциями, подготовленными запросами и как выбрать оптимальный подход в зависимости от требований проекта.

Библиотека database/sql и драйверы

Go предоставляет стандартный интерфейс для работы с базами данных через пакет database/sql. Однако он не включает в себя реализацию для конкретных СУБД (PostgreSQL, MySQL, SQLite и других), поэтому для работы с конкретной базой необходимо подключать соответствующий драйвер.

Примеры популярных драйверов:

  • PostgreSQLgithub.com/lib/pq
  • MySQLgithub.com/go-sql-driver/mysql
  • SQLitegithub.com/mattn/go-sqlite3

Драйверы реализуют интерфейс database/sql, что позволяет работать с разными базами данных схожим образом.

Подключение к базе данных

Подключение к базе выполняется через sql.Open(). Смотрите, как можно подключиться к PostgreSQL:

import (
    "database/sql"
    _ "github.com/lib/pq"
)

func main() {
    connStr := "user=username dbname=mydb sslmode=disable"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()
}

Обратите внимание, что sql.Open() не устанавливает реальное соединение, а только создает объект базы данных. Чтобы проверить подключение, используйте db.Ping().

if err := db.Ping(); err != nil {
    log.Fatal("Не удалось подключиться к базе данных:", err)
}

Это особенно важно для определения ошибок аутентификации или проблем с сетью.

Выполнение SQL-запросов

Для выполнения SQL-запросов используются методы:

  • Exec() – для запросов без результата (INSERT, UPDATE, DELETE).
  • Query() – для получения множества строк.
  • QueryRow() – для запроса одной строки.

Вот пример вставки данных в таблицу:

_, err := db.Exec("INSERT INTO users (name, age) VALUES ($1, $2)", "Alice", 30)
if err != nil {
    log.Fatal(err)
}

А теперь посмотрим, как извлекать данные:

rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    var age int
    if err := rows.Scan(&id, &name, &age); err != nil {
        log.Fatal(err)
    }
    fmt.Println(id, name, age)
}

Метод rows.Scan() считывает данные из результата запроса.

Подготовленные запросы

Использование подготовленных запросов (Prepared Statements) позволяет повысить безопасность и производительность.

stmt, err := db.Prepare("INSERT INTO users (name, age) VALUES ($1, $2)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close()

_, err = stmt.Exec("Bob", 25)
if err != nil {
    log.Fatal(err)
}

База данных кэширует запрос, что ускоряет повторное выполнение.

Транзакции в Go

Транзакции позволяют выполнять несколько операций как единое целое. Если одна из них завершится с ошибкой, все изменения откатятся (Rollback()), иначе — зафиксируются (Commit()).

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}

_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = $1", 1)
if err != nil {
    tx.Rollback()
    log.Fatal(err)
}

_, err = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = $1", 2)
if err != nil {
    tx.Rollback()
    log.Fatal(err)
}

err = tx.Commit()
if err != nil {
    log.Fatal(err)
}

Это полезно для финансовых операций и других критичных процессов.

Использование ORM: gorm

Если писать SQL-запросы вручную неудобно, можно использовать ORM-библиотеки. Самая популярная ORM в Go — gorm (gorm.io/gorm). Она позволяет работать с базой через структуры.

Пример использования gorm:

type User struct {
    ID   uint
    Name string
    Age  int
}

db.Create(&User{Name: "Charlie", Age: 28})

Go сам формирует SQL-запрос, что упрощает работу с базой данных.

Когда использовать database/sql, а когда gorm?

Каждый подход имеет свои преимущества:

  • database/sql подходит для высоконагруженных систем, где важен полный контроль над SQL-запросами. Он минималистичен и не добавляет лишних абстракций.
  • gorm удобен для быстрого прототипирования и проектов, где работа с данными похожа на объектно-ориентированное программирование. Однако он менее эффективен при сложных SQL-запросах.

Выбор зависит от требований вашего проекта.

Заключение

В Go можно работать с базами данных разными способами: через стандартную библиотеку database/sql или используя ORM-библиотеки.

Если вам нужен максимальный контроль и производительность, database/sql — лучший вариант. Но если важнее удобство и скорость разработки, стоит присмотреться к gorm.

Смотрите, что важно учитывать при выборе: если ваш проект предполагает сложные SQL-запросы и высокую нагрузку, database/sql обеспечит большую гибкость. Если же вам нужно быстро разрабатывать API с базой данных, ORM-библиотеки, такие как gorm, помогут сократить объем кода.

Независимо от выбранного подхода, стоит учитывать безопасность (использовать подготовленные запросы), следить за соединениями с базой данных и эффективно управлять транзакциями.

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile