Работа с базами данных в Go: PostgreSQL и MongoDB на практике

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

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

Начать курс

Введение: Почему Go и базы данных — идеальная пара

Язык программирования Go (Golang) завоевал популярность благодаря высокой производительности, встроенной поддержке конкурентности и простоте развертывания. Однако настоящая сила Go раскрывается при работе с базами данных. Будь то реляционная PostgreSQL или документо-ориентированная MongoDB, Go предоставляет элегантные инструменты для эффективного взаимодействия с данными.

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

1. Работа с PostgreSQL в Go

Для работы с PostgreSQL в Go используется стандартный пакет database/sql в паре с драйвером pgx или lib/pq. pgx рекомендуется из-за лучшей производительности и поддержки расширенных возможностей PostgreSQL.

1.1. Установка и подключение

Установите драйвер через go modules:

go get github.com/jackc/pgx/v5

Пример подключения к базе данных:

package main

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v5/pgxpool"
    "log"
)

func main() {
    dsn := "postgres://user:password@localhost:5432/mydb?sslmode=disable"
    pool, err := pgxpool.New(context.Background(), dsn)
    if err != nil {
        log.Fatal("Unable to connect to database:", err)
    }
    defer pool.Close()

    fmt.Println("Connected to PostgreSQL!")
}

Совет: Используйте пул соединений (pgxpool) для production-среды — это уменьшает накладные расходы на установку новых соединений.

1.2. CRUD операции

Рассмотрим базовые операции на примере таблицы users:

// CREATE
func createUser(pool *pgxpool.Pool, name string, email string) (int, error) {
    var id int
    err := pool.QueryRow(context.Background(),
        "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
        name, email).Scan(&id)
    if err != nil {
        return 0, err
    }
    return id, nil
}

// READ
func getUser(pool *pgxpool.Pool, id int) (string, string, error) {
    var name, email string
    err := pool.QueryRow(context.Background(),
        "SELECT name, email FROM users WHERE id = $1", id).Scan(&name, &email)
    if err != nil {
        return "", "", err
    }
    return name, email, nil
}

// UPDATE
func updateUserEmail(pool *pgxpool.Pool, id int, newEmail string) error {
    _, err := pool.Exec(context.Background(),
        "UPDATE users SET email = $1 WHERE id = $2", newEmail, id)
    return err
}

// DELETE
func deleteUser(pool *pgxpool.Pool, id int) error {
    _, err := pool.Exec(context.Background(),
        "DELETE FROM users WHERE id = $1", id)
    return err
}

Обратите внимание на использование параметризованных запросов с $1, $2 — это защищает от SQL-инъекций.

1.3. Транзакции

PostgreSQL поддерживает транзакции, что критично для целостности данных:

func transferMoney(pool *pgxpool.Pool, fromID, toID int, amount float64) error {
    tx, err := pool.Begin(context.Background())
    if err != nil {
        return err
    }
    defer tx.Rollback(context.Background()) // откат при ошибке

    _, err = tx.Exec(context.Background(),
        "UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID)
    if err != nil {
        return err
    }

    _, err = tx.Exec(context.Background(),
        "UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID)
    if err != nil {
        return err
    }

    return tx.Commit(context.Background())
}

2. Работа с MongoDB в Go

MongoDB — документо-ориентированная NoSQL база данных. Официальный драйвер mongo-go-driver обеспечивает полную поддержку всех возможностей MongoDB.

2.1. Установка и подключение

go get go.mongodb.org/mongo-driver/mongo

Пример подключения:

package main

import (
    "context"
    "fmt"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "log"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctx)

    fmt.Println("Connected to MongoDB!")
}

2.2. CRUD операции с документами

MongoDB работает с коллекциями (аналог таблиц) и документами (аналог строк в BSON-формате).

type User struct {
    ID    primitive.ObjectID `bson:"_id,omitempty"`
    Name  string             `bson:"name"`
    Email string             `bson:"email"`
}

// CREATE
func createUser(client *mongo.Client, user User) (*mongo.InsertOneResult, error) {
    collection := client.Database("mydb").Collection("users")
    result, err := collection.InsertOne(context.Background(), user)
    return result, err
}

// READ
func getUserByID(client *mongo.Client, id primitive.ObjectID) (User, error) {
    collection := client.Database("mydb").Collection("users")
    var user User
    err := collection.FindOne(context.Background(),
        bson.M{"_id": id}).Decode(&user)
    return user, err
}

// UPDATE
func updateUserEmail(client *mongo.Client, id primitive.ObjectID, newEmail string) error {
    collection := client.Database("mydb").Collection("users")
    _, err := collection.UpdateOne(context.Background(),
        bson.M{"_id": id},
        bson.M{"$set": bson.M{"email": newEmail}})
    return err
}

// DELETE
func deleteUser(client *mongo.Client, id primitive.ObjectID) error {
    collection := client.Database("mydb").Collection("users")
    _, err := collection.DeleteOne(context.Background(), bson.M{"_id": id})
    return err
}

2.3. Индексы и агрегации

Для производительности обязательно создавайте индексы:

func createEmailIndex(client *mongo.Client) error {
    collection := client.Database("mydb").Collection("users")
    indexModel := mongo.IndexModel{
        Keys:    bson.M{"email": 1},
        Options                

Блоги

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