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

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

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

Начать курс

Введение в sqlx: мощное расширение database/sql для Go

Библиотека sqlx — это популярное расширение стандартного пакета database/sql в языке Go. Она не заменяет стандартный драйвер, а надстраивается поверх него, добавляя удобные функции для работы с базами данных. Если вы когда-либо писали на Go и сталкивались с необходимостью вручную сканировать строки результата запроса в структуры, то sqlx покажется вам настоящим спасением.

Основная цель sqlx — упростить и ускорить разработку, избавив разработчика от шаблонного кода. Библиотека позволяет:

  • Автоматически маппить результаты SQL-запросов в Go-структуры.
  • Работать с именованными параметрами в запросах.
  • Выполнять массовые вставки данных (batch insert).
  • Использовать удобные методы для работы с prepared statements.
  • Интегрироваться с любыми драйверами, совместимыми с database/sql (PostgreSQL, MySQL, SQLite, и др.).

На сайте PythonLib.ru мы часто рассказываем о полезных инструментах для разработчиков. Сегодня мы подробно разберем sqlx — библиотеку, которая обязательна к изучению каждому Go-разработчику, работающему с базами данных.

Установка sqlx

Установка sqlx в проект на Go выполняется стандартным способом через менеджер пакетов go get:

go get github.com/jmoiron/sqlx

Также вам потребуется установить драйвер для конкретной базы данных. Например, для PostgreSQL:

go get github.com/lib/pq

Для MySQL:

go get github.com/go-sql-driver/mysql

Для SQLite:

go get github.com/mattn/go-sqlite3

После установки вы можете импортировать библиотеку в свой код:

import (
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq" // драйвер PostgreSQL
)

Быстрый старт: первый пример с sqlx

Давайте напишем простую программу, которая подключается к базе данных PostgreSQL, создает таблицу, вставляет несколько записей и выводит их на экран.

package main

import (
    "fmt"
    "log"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

type User struct {
    ID   int    `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func main() {
    // Подключение к базе данных
    db, err := sqlx.Connect("postgres", "user=postgres dbname=testdb sslmode=disable")
    if err != nil {
        log.Fatalln(err)
    }
    defer db.Close()

    // Создание таблицы
    schema := `CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        age INTEGER NOT NULL
    );`
    db.MustExec(schema)

    // Вставка данных
    db.MustExec("INSERT INTO users (name, age) VALUES ($1, $2)", "Alice", 30)
    db.MustExec("INSERT INTO users (name, age) VALUES ($1, $2)", "Bob", 25)

    // Выборка данных
    var users []User
    err = db.Select(&users, "SELECT id, name, age FROM users")
    if err != nil {
        log.Fatalln(err)
    }

    fmt.Printf("Users: %+v\n", users)
}

В этом примере мы видим ключевые возможности sqlx: автоматическое маппинг полей структуры (благодаря тегам db), метод Select для выборки нескольких строк, и MustExec для выполнения запросов без обработки ошибок (удобно для прототипирования).

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

Рассмотрим 10 ключевых методов библиотеки sqlx с подробным описанием и примерами.

1. sqlx.Connect

Что делает: Устанавливает соединение с базой данных и проверяет его (выполняет ping). Это самый удобный способ подключения.

Параметры:

  • driverName (string) — имя драйвера (например, "postgres", "mysql").
  • dataSourceName (string) — строка подключения к БД.

Пример:

db, err := sqlx.Connect("postgres", "user=postgres dbname=mydb sslmode=disable")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

2. sqlx.Open

Что делает: Создает пул соединений, но не проверяет подключение. Аналог database/sql.Open, но возвращает *sqlx.DB.

Параметры: Те же, что и у Connect.

Пример:

db, err := sqlx.Open("postgres", "user=postgres dbname=mydb sslmode=disable")
if err != nil {
    log.Fatal(err)
}
// Проверка соединения вручную
err = db.Ping()
if err != nil {
    log.Fatal(err)
}

3. DB.Select

Что делает: Выполняет запрос и маппит все строки результата в срез структур. Один из самых часто используемых методов.

Параметры:

  • dest (interface{}) — указатель на срез структур.
  • query (string) — SQL-запрос.
  • args (...interface{}) — аргументы запроса.

Пример:

var users []User
err := db.Select(&users, "SELECT * FROM users WHERE age > $1", 18)
if err != nil {
    log.Fatal(err)
}
for _, u := range users {
    fmt.Println(u.Name)
}

4. DB.Get

Что делает: Выполняет запрос и маппит первую строку результата в структуру. Если строк нет — возвращает ошибку sql.ErrNoRows.

Параметры:

  • dest (interface{}) — указатель на структуру.
  • query (string) — SQL-запрос.
  • args (...interface{}) — аргументы запроса.

Пример:

var user User
err := db.Get(&user, "SELECT * FROM users WHERE id = $1", 1)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("User: %+v\n", user)

5. DB.Exec

Что делает: Выполняет запрос, не возвращающий строки (INSERT, UPDATE, DELETE). Возвращает результат выполнения.

Параметры:

  • query (string) — SQL-запрос.
  • args (...interface{}) — аргументы запроса.

Пример:

result, err := db.Exec("UPDATE users SET age = $1 WHERE id = $2", 35, 1)
if err != nil {
    log.Fatal(err)
}
rowsAffected, _ := result.RowsAffected()
fmt.Printf("Обновлено строк: %d\n", rowsAffected)

6. DB.MustExec

Что делает: Аналог Exec, но паникует при ошибке. Удобно для инициализации схемы или тестов.

Параметры: Те же, что у Exec.

Пример:

db.MustExec("CREATE TABLE IF NOT EXISTS items (id SERIAL PRIMARY KEY, name TEXT)")
db.MustExec("INSERT INTO items (name) VALUES ($1)", "Laptop")

7. DB.NamedExec

Что делает: Выполняет запрос с именованными параметрами. Позволяет использовать имена полей структуры вместо позиционных плейсхолдеров.

Параметры:

  • query (string) — SQL-запрос с именованными параметрами (например, :name).
  • arg (interface{}) — структура или карта с данными.

Пример:

type Product struct {
    Name  string `db:"name"`
    Price float64 `db:"price"`
}

product := Product{Name: "Phone", Price: 999.99}
db.NamedExec("INSERT INTO products (name, price) VALUES (:name, :price)", product)

8. DB.NamedQuery

Что делает: Выполняет запрос с именованными параметрами и возвращает строки для итерации.

Параметры:

  • query (string) — SQL-запрос.
  • arg (interface{}) — структура или карта.

Пример:

rows, err := db.NamedQuery("SELECT * FROM products WHERE price > :minPrice", map[string]interface{}{
    "minPrice": 500,
})
if err != nil {
    log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
    var p Product
    err := rows.StructScan(&p)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(p.Name)
}

9. DB.PrepareNamed

Что делает: Создает подготовленный запрос с именованными параметрами. Возвращает *sqlx.NamedStmt.

Параметры:

  • query (string) — SQL-запрос.

Пример:

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

user := User{Name: "Charlie", Age: 28}
stmt.Exec(user)

10. sqlx.In

Что делает: Вспомогательная функция для построения запросов с динамическим списком IN. Позволяет безопасно передавать срез значений.

Параметры:

  • query (string) — SQL-запрос с плейсхолдером ? или $1.
  • args (...interface{}) — аргументы, включая срезы.

Пример:

ids := []int{1, 2, 3, 5}
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
if err != nil {
    log.Fatal(err)
}
// Для PostgreSQL нужно переформатировать плейсхолдеры
query = db.Rebind(query)

var users []User
err = db.Select(&users, query, args...)
if err != nil {
    log.Fatal(err)
}

Реальный практический пример: CRUD-приложение для управления задачами

Давайте создадим полноценное консольное приложение для управления списком задач (TODO). Мы будем использовать PostgreSQL и sqlx.

package main

import (
    "fmt"
    "log"
    "os"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

type Task struct {
    ID        int    `db:"id"`
    Title     string `db:"title"`
    Completed bool   `db:"completed"`
}

var db *sqlx.DB

func initDB() {
    var err error
    db, err = sqlx.Connect("postgres", "user=postgres dbname=todoapp sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }

    schema := `CREATE TABLE IF NOT EXISTS tasks (
        id SERIAL PRIMARY KEY,
        title TEXT NOT NULL,
        completed BOOLEAN DEFAULT FALSE                    

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